Subquery com join entre dois ou mais campos - Criteria

Pessoal,
Primeiramente, seguem abaixo classe e tabela:

CLASSE:


@Entity
@Table(name="fin_LancamentosDespesas")
public class LancamentoDespesa extends MappedEntity{


	private static final long serialVersionUID = 2114159600713444333L;
	
	
	private Integer numeroLancamento;
	
	private Date dataCompetencia;
	
	private Date dataEmissao;
	
	private String observacao;
	
	private String numeroDocumento;
	
	private Date dataOperacao;
	
	private UsuarioSIG responsavelOperacao;
	
	private Boolean flagCaixa;
	
	private Double valorBruto;
	
	private Double valorLiquido;
	
	private UsuarioSIG responsavelConferencia;
	
	private Fornecedor fornecedor;
	
	private TipoDocumento tipoDocumento;
	
	private Date dataConferencia;	
	
	private Empresa empresa;
	
	private TipoOrigem tipoOrigem;
	
	private Date dataCancelamento;
	
	private List<ParcelaLancamentoDespesa> listaParcela;	
	
	private List<RateioLancamento> listaRateio;	
	
	private List<RetencaoLancamentoDespesa> listaRetencao;
	
		

	@Id
	@Override
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "NUMG_LancamentoDespesa")
	public Long getId() {
		return super.getId();
	}
	
	@Column(name="NUMR_LancamentoDespesa")
	public Integer getNumeroLancamento() {
		return numeroLancamento;
	}

	public void setNumeroLancamento(Integer numeroLancamento) {
		this.numeroLancamento = numeroLancamento;
	}

	@Column(name="DATA_Cancelamento")
	@Temporal(TemporalType.TIMESTAMP)
	public Date getDataCancelamento() {
		return dataCancelamento;
	}

	public void setDataCancelamento(Date dataCancelamento) {
		this.dataCancelamento = dataCancelamento;
	}
	
	@Column(name = "DATA_Competencia")
	@NotNull(message = "A data competência do lançamento deve ser preenchida.")
	public Date getDataCompetencia() {
		return dataCompetencia;
	}

	public void setDataCompetencia(Date dataCompetencia) {
		this.dataCompetencia = dataCompetencia;
	}

	@Column(name = "DATA_Emissao")
	@NotNull(message = "A data emissão do lançamento deve ser preenchida.")
	@Temporal(TemporalType.DATE)
	public Date getDataEmissao() {
		return dataEmissao;
	}

	public void setDataEmissao(Date dataEmissao) {
		this.dataEmissao = dataEmissao;
	}
	
	@Column(name = "DESC_Observacao")
	public String getObservacao() {
		return observacao;
	}

	public void setObservacao(String observacao) {
		this.observacao = observacao;
	}

	@Column(name = "CODG_Documento")
	@NotNull(message = "O numero documeto do lançamento deve ser preenchido.")
	public String getNumeroDocumento() {
		return numeroDocumento;
	}

	public void setNumeroDocumento(String numeroDocumento) {
		this.numeroDocumento = numeroDocumento;
	}

	@Column(name = "DATA_Operacao")
	@NotNull(message = "A data de operação do lançamento deve ser preenchida.")
	public Date getDataOperacao() {
		return dataOperacao;
	}

	public void setDataOperacao(Date dataOperacao) {
		this.dataOperacao = dataOperacao;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "NUMG_UsuarioOperacao")
	@NotNull(message = "O responsável operação do lançamento deve ser preenchido.")
	public UsuarioSIG getResponsavelOperacao() {
		return responsavelOperacao;
	}

	public void setResponsavelOperacao(UsuarioSIG responsavelOperacao) {
		this.responsavelOperacao = responsavelOperacao;
	}

	@Column(name = "FLAG_Caixa")
	@NotNull(message = "O flag caixa do lançamento deve ser preenchido.")
	public Boolean getFlagCaixa() {
		return flagCaixa;
	}

	public void setFlagCaixa(Boolean flagCaixa) {
		this.flagCaixa = flagCaixa;
	}

	@Column(name = "VALR_Bruto")
	@NotNull(message = "O valor bruto do lançamento deve ser preenchido.")
	public Double getValorBruto() {
		return valorBruto;
	}

	public void setValorBruto(Double valorBruto) {
		this.valorBruto = valorBruto;
	}

	@Column(name = "VALR_Liquido")
	@NotNull(message = "O valor líquido do lançamento deve ser preenchido.")
	public Double getValorLiquido() {
		return valorLiquido;
	}

	public void setValorLiquido(Double valorLiquido) {
		this.valorLiquido = valorLiquido;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "NUMG_UsuarioConferencia")	
	public UsuarioSIG getResponsavelConferencia() {
		return responsavelConferencia;
	}

	public void setResponsavelConferencia(UsuarioSIG responsavelConferencia) {
		this.responsavelConferencia = responsavelConferencia;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "NUMG_Fornecedor")
	@NotNull(message = "O fornecedor do lançamento deve ser preenchido.")
	public Fornecedor getFornecedor() {
		return fornecedor;
	}

	public void setFornecedor(Fornecedor fornecedor) {
		this.fornecedor = fornecedor;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "NUMG_DocumentoTipo")
	@NotNull(message = "O tipo documento do lançamento deve ser preenchido.")
	public TipoDocumento getTipoDocumento() {
		return tipoDocumento;
	}

	public void setTipoDocumento(TipoDocumento tipoDocumento) {
		this.tipoDocumento = tipoDocumento;
	}

	@Column(name = "DATA_Conferencia")	
	@Temporal(TemporalType.TIMESTAMP)
	public Date getDataConferencia() {
		return dataConferencia;
	}

	public void setDataConferencia(Date dataConferencia) {
		this.dataConferencia = dataConferencia;
	}
	
	@OneToMany(mappedBy = "lancamentoDespesa", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	public List<ParcelaLancamentoDespesa> getListaParcela() {
		if (listaParcela == null){
			listaParcela = new ArrayList<ParcelaLancamentoDespesa>();
		}
		return listaParcela;
	}	
	
	public void setListaParcela(List<ParcelaLancamentoDespesa> listaParcela) {
		this.listaParcela = listaParcela;
	}
	
	@OneToMany(mappedBy = "lancamentoDespesa", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	public List<RateioLancamento> getListaRateio() {
		if (listaRateio == null){
			listaRateio = new ArrayList<RateioLancamento>();
		}
		return listaRateio;
	}

	public void setListaRateio(List<RateioLancamento> listaRateio) {
		this.listaRateio = listaRateio;
	}

	@OneToMany(mappedBy = "lancamentoDespesa", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	public List<RetencaoLancamentoDespesa> getListaRetencao() {
		if (listaRetencao == null){
			listaRetencao = new ArrayList<RetencaoLancamentoDespesa>();
		}
		return listaRetencao;
	}

	public void setListaRetencao(List<RetencaoLancamentoDespesa> listaRetencao) {
		this.listaRetencao = listaRetencao;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "NUMG_Empresa")
	@NotNull(message = "A empresa do lançamento deve ser preenchida.")
	public Empresa getEmpresa() {
		return empresa;
	}

	public void setEmpresa(Empresa empresa) {
		this.empresa = empresa;
	}	

	@Column(name = "TIPO_Origem")
	@Type(type = "org.hibernate.usertype.TipoOrigemUserType")
	@NotNull(message = "A origem do Lancamento Deve se informada.")
	public TipoOrigem getTipoOrigem() {
		return tipoOrigem;
	}

	public void setTipoOrigem(TipoOrigem tipoOrigem) {
		this.tipoOrigem = tipoOrigem;
	}
	
	
	
}

TABELA

Column_name	Type
NUMG_LancamentoDespesa	int
NUMR_Versao	smallint
DATA_Competencia	smalldatetime
DATA_Emissao	datetime
DESC_Observacao	varchar
CODG_Documento	varchar
DATA_Operacao	datetime
NUMG_UsuarioOperacao	int
FLAG_Caixa	bit
VALR_Bruto	money
VALR_Liquido	money
NUMG_UsuarioConferencia	int
DATA_Conferencia	datetime
NUMG_Fornecedor	int
NUMG_DocumentoTipo	tinyint
NUMG_Empresa	smallint
TIPO_Origem	tinyint
NUMR_LancamentoDespesa	int
DATA_Cancelamento	datetime

Estou procurando exemplos de criteria utilizando subquery mas só achei exemplos onde o join é somente com uma coluna, tipo (código 1):

			SELECT ld.* 
			FROM dbo.fin_LancamentosDespesas ld 
			INNER JOIN (
			SELECT MAX(DATA_Operacao) AS DATA_Operacao 
			FROM dbo.fin_LancamentosDespesas 
			WHERE NUMG_Empresa = 70
			 AND CODG_Documento = '210109-002'
			 AND DATA_Emissao = '2009-01-21 00:00:00.000'
			 AND NUMG_Fornecedor = 58
			 AND NUMG_DocumentoTipo = 5
			 AND DATA_Cancelamento = '1900-01-01 00:00:00.000' 
			GROUP BY NUMR_LancamentoDespesa) ldt  
			ON ld.DATA_Operacao = ldt.DATA_Operacao

O que estou precisando é de um join entre dois campos (código 2):

			SELECT ld.* 
			FROM dbo.fin_LancamentosDespesas ld 
			INNER JOIN (
			SELECT MAX(DATA_Operacao) AS DATA_Operacao, NUMR_LancamentoDespesa  
			FROM dbo.fin_LancamentosDespesas 
			WHERE NUMG_Empresa = 70
			 AND CODG_Documento = '210109-002'
			 AND DATA_Emissao = '2009-01-21 00:00:00.000'
			 AND NUMG_Fornecedor = 58
			 AND NUMG_DocumentoTipo = 5
			 AND DATA_Cancelamento = '1900-01-01 00:00:00.000' 
			GROUP BY NUMR_LancamentoDespesa) ldt  
			ON ld.NUMR_LancamentoDespesa = ldt.NUMR_LancamentoDespesa 
			AND ld.DATA_Operacao = ldt.DATA_Operacao

O máximo que consegui chegar com criteria foi (código 3):

    select
        this_.*
    from
        dbo.fin_LancamentosDespesas this_ 
    where
        exists (
            select
                max(this0__.DATA_Operacao) as y0_,
                this0__.NUMR_LancamentoDespesa as y1_ 
            from
                dbo.fin_LancamentosDespesas this0__ 
            where
				this_.CODG_Documento='210109-002' 
				and this_.DATA_Emissao='2009-01-21 00:00:00.000' 
				and this_.NUMG_Fornecedor=58
				and this_.NUMG_Empresa=70
				and this_.NUMG_DocumentoTipo = 5
				and this_.DATA_Cancelamento='1900-01-01 00:00:00.000'
                and this0__.DATA_Operacao=this_.DATA_Operacao 
                and this0__.NUMR_LancamentoDespesa=this_.NUMR_LancamentoDespesa
            group by
                this0__.NUMR_LancamentoDespesa
        )

E o código da criteria utilizado foi (código 4):

			DetachedCriteria dc = DetachedCriteria.forClass(LancamentoDespesa.class,"ld");
			dc.setProjection(
			  Projections.projectionList()
			    .add(Projections.max("ld.dataOperacao"))
			    .add(Projections.groupProperty("ld.numeroLancamento"))
			);
			dc.add(Restrictions.eq("ld.numeroDocumento", numeroDocumento));
			dc.add(Restrictions.eq("ld.dataEmissao", dataEmissao));
			dc.add(Restrictions.eq("ld.fornecedor", fornecedor));
			dc.add(Restrictions.eq("ld.empresa", empresa));
			dc.add(TSQLFunctions.is1900("ld.dataCancelamento"));
			dc.add(Property.forName("ld.dataOperacao").eqProperty("ld1.dataOperacao"));
			dc.add(Property.forName("ld.numeroDocumento").eqProperty("ld1.numeroDocumento"));

			Criteria c = session.createCriteria(LancamentoDespesa.class,"ld1")
				.add(Subqueries.exists(dc));

Gostaria de saber se há uma forma de conseguir gerar o SQL do código 2.

Obrigado.