O Criteria é uma API do Hibernate que facilita muito quando precisamos montar uma query complexa com filtros opcionais. Adicionar restrições ou criar joins com esta API é muito mais simples de gerenciar do que concatenando Strings, como faríamos ao trabalhar com SQL puro.
Apesar das vantagens, o Criteria também tem alguns problemas. O último que encontrei foi ao tentar fazer 2 joins entre as mesmas 2 tabelas. No meu caso, eu tinha no banco as tabelas projeto e historico. A segunda tabela é populada através de uma trigger no banco: sempre que o status do projeto muda, a tabela historico registra o status anterior do projeto com data/hora da mudança. Eu precisava fazer uma query que buscasse um projeto com status “iniciado” num determinado período de datas e com status “finalizado” em outro período. Inicialmente, pensei simplesmente em criar 2 joins entre as tabelas, cada um com um alias diferente e filtrando pelas datas específicas:
Criteria criteria = getSession().createCriteria(Projeto.class);
// Primeiro join
criteria.createCriteria("historicoList", "historicoIniciado", Criteria.LEFT_JOIN)
.add(Restrictions.eq("historicoIniciado.status", Status.INICIADO.value()))
.add(Restrictions.ge("historicoIniciado.data", dataIniciadoDe))
.add(Restrictions.le("historicoIniciado.data", dataIniciadoAte));
// Segundo join
criteria.createCriteria("historicoList", "historicoFinalizado", Criteria.LEFT_JOIN)
.add(Restrictions.eq("historicoFinalizado.status", Status.FINALIZADO.value()))
.add(Restrictions.ge("historicoFinalizado.data", dataFinalizadoDe))
.add(Restrictions.le("historicoFinalizado.data", dataFinalizadoAte));
O código acima, apesar de semelhante ao que eu já havia criado para adicionar outros filtros à query de projetos, fazendo joins com outras tabelas, não funcionava. Tentei retirar um dos joins com a tabela historico e funcionou. Ou seja, o problema estava na criação do segundo join com as mesmas tabelas, mesmo utilizando aliases diferentes. Ao pesquisar este problema, descobri que não é um bug. Na verdade, o Criteria não suporta múltiplos joins para a mesma associação.
Sendo assim, a solução que encontrei para este problema foi criar uma subquery para a tabela historico, utilizando um DetachedCriteria:
DetachedCriteria historicoCriteria = DetachedCriteria.forClass(Historico.class, "historicoIniciado")
.setProjection(Projections.distinct(Projections.property("projeto")))
.add(Restrictions.eq("historicoIniciado.status", Status.INICIADO.value()));
.add(Restrictions.ge("historicoIniciado.data", dataIniciadoDe));
.add(Restrictions.le("historicoIniciado.data", dataIniciadoAte));
criteria.add(Subqueries.propertyIn("id", historicoCriteria));
Desta forma, apenas um dos joins precisa ser substituído por uma subquery. O outro join pode ser mantido sem problemas.


Fala meu amigo.
Não sei se meu problema era semelhante ao seu, mas vou postar uma solução, em que consegui fazer o JOIN entre 4 tabelas.
Segue:
Eu tenho as tabelas, vou colocar so os dados que interessam
– EntrevistadorPesquisa(id, (FK)idPesquisa, (FK)idEntrevistador)
– Entrevistador(id, (FK)idUsuario, (FK)idNivel)
– Usuario(id)
– NivelEntrevistador(id, descricao)
ps: todos id’s são P.Keys.
ps: 2, tudo que for CAIXA ALTA a partir de agora é tabela
Sendo assim, eu acesso a tabela ENTREVISTADOR_PESQUISA, pra saber quais entrevistadores estão relacionados a quais pesquisas.
Preciso então cruzar essa ultima com ENTREVISTADOR, pra ter os dados do entrevistador. Preciso cruzar ENTREVISTADOR com USUARIO, para eu ter acesso a dados de login, senha, ativo…etc.
E preciso cruzar ENTREVISTADOR com NIVEL_ENTREVISTADOR, pra saber qual o nivel de cada entrevistador.
Sendo assim, com o join da forma que eu estava tentando
session.createCriteria(EntrevistadorPesquisa.class)
.createCriteria(“entrevistador”)
.createCriteria(“usuario”);
.createCriteria(“nivelEntrevistador”)
eu teria meus dados não mão, correto?
NÃO, ERRADO.
Como vc disse, por padrão o hibernate não deixa eu fazer isso, pois consome muita memória, por muitas vezes atoa.
Pois então é preciso mudar a propriedade @ManyToOne da tabela Entrevistador, de LAZY para EAGER.
Sendo assim, só mudei minha configuração:
– @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = “ID_USUARIO”, nullable = false)
public Usuario getUsuario() {
return this.usuario;
}
– @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = “ID_NIVEL”, nullable = false)
public NivelEntrevistador getNivelEntrevistador() {
return this.nivelEntrevistador;
}
e obtive sucesso na minha consulta.
O que achou? O problema era semelhante mesmo? Espero que tenha sido útil a resposta
Aguardo uma resposta por email, pra gente trocar idéia. Obrigado e até mais.
Ânderson, se eu entendi bem, acho que não é o mesmo caso. No exemplo que eu citei, eu tinha 2 tabelas e precisava fazer 2 joins entre elas. No seu caso são várias tabelas diferentes, e isso o Hibernate suporta bem.
Só uma observação: o FetchType.EAGER não deve ser usado em relacionamentos OneToMany, pois cada vez que você buscar um registro, buscará também uma lista de elementos da outra tabela, o que pode provocar um impacto considerável no desempenho da sua aplicação.
Sim. Nesse caso, o EAGER soloucionou bem meu caso, ou existe alguma forma melhor?
No seu caso não há problema, pois o EAGER foi colocado no @ManyToOne. O problema é quando ele é usado no @OneToMany, ou seja, na outra ponta do relacionamento, onde o EAGER faria com que um select buscasse também uma lista de objetos da outra entidade.
Olá amigo,
Aqui onde trabalho, a gente utiliza dessa forma e funciona;
Criteria criteria = getSession().createCriteria( ProdutoProjetoVO.class, “produtoProjeto” );
criteria.createAlias(“produtoProjeto.projetoTipoProduto.projeto”, “p_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.projetoTipoProduto.tipoProduto”, “tP_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.projetoTipoProduto.tipoProduto.statusProduto”, “tpS_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.projetoTipoProduto”, “pTP_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.statusProduto”, “sP_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.faseProjeto”, “fP_”, JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.projetoTipoProduto.projeto.empreendimento”, “e_”,JoinFragment.INNER_JOIN);
criteria.createAlias(“produtoProjeto.projetoTipoProduto.projeto.empreendimento.areaGerencial”, “a_”,JoinFragment.INNER_JOIN);
ProjectionList pList = getDefaultProjectionList()
.add(Projections.property( “e_.id” ).as( “projetoTipoProduto.projeto.empreendimento.id” ) )
.add(Projections.property( “e_.sigla” ).as( “projetoTipoProduto.projeto.empreendimento.sigla” ) )
.add(Projections.property( “e_.nome” ).as( “projetoTipoProduto.projeto.empreendimento.nome” ) )
.add(Projections.property( “a_.id” ).as( “projetoTipoProduto.projeto.empreendimento.areaGerencial.id” ) )
.add(Projections.property( “a_.nome” ).as( “projetoTipoProduto.projeto.empreendimento.areaGerencial.nome” ) )
.add(Projections.property(“tpS_.id”).as(“projetoTipoProduto.tipoProduto.statusProduto.id”))
.add(Projections.property(“tpS_.descricao”).as(“projetoTipoProduto.tipoProduto.statusProduto.descricao”))
.add(Projections.property( “p_.tipoProjeto” ).as(“projetoTipoProduto.projeto.tipoProjeto”));
criteria.setProjection(pList);
criteria.add( Restrictions.ne( “sP_.id”, StatusProdutoVO.EXPIRADO ) );
criteria.add( Restrictions.or( Restrictions.eq( “tP_.id”, TipoProdutoVO.HISTOGRAMA_PLANEJADO ),
Restrictions.eq( “tP_.id”, TipoProdutoVO.HISTOGRAMA_REALIZADO ) ) );
criteria.add( Restrictions.eq( “fP_.id”, FaseProjetoVO.FASE_4 ) );
criteria.add( Restrictions.eq( “p_.tipoProjeto”, ProjetoVO.TIPO_PROJETO ) );
if ( empreendimento != null && empreendimento.getAreaGerencial() != null &&
empreendimento.getAreaGerencial().getId() != null && empreendimento.getAreaGerencial().getId() > 0 ){
criteria.add( Restrictions.eq( “a_.id”, empreendimento.getAreaGerencial().getId() ) );
}
if ( idProjeto != null && idProjeto > 0 ){
criteria.add( Restrictions.eq( “p_.id”, idProjeto ) );
}
if ( empreendimento != null && empreendimento.getId() != null && empreendimento.getId() > 0 ){
criteria.add( Restrictions.eq( “e_.id”, empreendimento.getId() ) );
}
criteria.addOrder( Order.asc( “e_.nome” ) );
criteria.addOrder( Order.asc( “p_.nome” ) );
criteria.addOrder( Order.asc( “tP_.nome” ) );
criteria.addOrder( Order.desc( “produtoProjeto.versao” ) );
HibernateTransformerVO transformer = new HibernateTransformerVO(ProdutoProjetoVO.class);
criteria.setResultTransformer(transformer);
return (Collection) criteria.list();
Abraços
Se eu entendi bem o seu código, não é o mesmo caso. No seu exemplo, você procura por projetos com tipo = HISTOGRAMA_PLANEJADO ou HISTOGRAMA_REALIZADO, certo?
No exemplo que citei no post, eu procurava por projetos com histórico iniciado ou finalizado, mas precisava definir valores diferentes de data para cada caso (se for histórico iniciado, a data deve ser entre dataIniciadoDe e dataIniciadoAte; se for histórico finalizado, a data deve ser entre dataFinalizadoDe e dataFinalizadoAte).