Populando apenas o necessário com Hibernate
Uma das funcionalidades do Hibernate é transformar os dados do banco em objeto (s) de acordo com as informações solicitadas, lhe poupando do trabalho de popula-los. Por padrão, o Hibernate popula todas os atributos do seu objeto, o que nem sempre é o desejado. Pense em um cenário no qual desejamos retornar ao frontend apenas os dados referente a identificação daquele objeto, como id e descrição, para popular um combo por exemplo. Não desejamos que o Hibernate retorne todos os atributos da classe gerando, assim, um trafego desnecessario em nossa rede.
Exemplificando, vamos imaginar que eu tenha uma classe de Produto que tenha dezenas de atributos. Não quero que o Hibernate popule todos eles, apenas o id e o nome do produto para enviar ao frontend. Para isso vamos utilizar 2 recursos: O setProjection, com a funcionalidade de projetar o que será requisitado na clausula select que, por padrão, sempre retorna os dados projetados como um array de objetos ao invés do próprio objeto (Produto); e o segundo recurso, setResultTransformer, que diz ao Hibernate como tratar os dados solicitados.
-
.createCriteria(Produto.class, "p")
-
.setProjection( Projections.projectionList()
-
.add( Projections.property("p.id").as("id") )
-
.add( Projections.property("p.nome").as("nome") )
-
)
-
.setResultTransformer(new AliasToBeanResultTransformer(Produto.class))
-
.list();
No exemplo acima, ao utilizarmos o método as("atributo"), criamos uma espécie de "alias" para o Hibernate saber em que propriedade ele irá "jogar" esse dado. Tal recurso é muito interessante quando desejamos selecionar dados de uma Classe e atribui-los diretamente a um DTO ou uma classe de relatório. O AliasToBeanResultTransformer pega todos os "alias" criados apartir do .as e atribui na classe que está sendo passada procurando pelo nome do atributo na mesma.
[update data="10/08/2007 14:15"]
Adicionado alias na query
[/update
15 comentários para “Populando apenas o necessário com Hibernate”
Daniel, o hibernate vai gerar um “SELECT * FROM …” ?
Parabéns pelo post.
Nao! Ele vai gerar um select apenas com os atributos que foram projetados. Projections.property(”atributo”)
[...] Muitas pessoas reclamam que perdem o poder do SQL ao utilizar um mapeamento objeto-relacional como o Hibernate. Daniel Passos mostra que existem modos de “recuperar” este “poder”, caso necessário, claro. [...]
Muito legal, resta lembrar que isso é para casos excepcionais…MUITO excepcionais
[]s
Essa feature é muito boa!
Infelizmente ainda tenho que usar Hibernate 2 e várias vezes tive que fazer um session.connection() e trabalhar com o bom e velho JDBC por questões de performace, principalmente em updates
No Hibernate 3 acredito que praticamente todos os problemas desse tipo foram resolvidos 
Dá para fazer isso via HQL também:
1. Primeiro, vc cria um construtor para receber exatamente os dados retornados:
public Produto(Long id, String nome) {
this.id = id;
this.nome = nome;
}
2. Depois usa esse construtor em uma HQL via “new”:
select new Produto(pd.id, p.nome) from Produto p where …
Realmente muito poderoso.
valeuz…
Se a sua classe Produto.class tivesse uma Collection, como fazer para que o setResultTransformer coloque ou a referencia do proxy da coleção em cada instancia de produto, ou já coloque a coleção instanciada ?
Valeu
Simplesmente maravilhoso!!!
Parabens!!!
Marcos,
Ate onde eu sei sempre que se utilizar uma projeção o hibernate ignora as associações que resultam mapeadas como uma coleção. Se vc prestar atenção na query gerada ou debugar o codigo, vera que ele ate seta a coleção para ser projetada mas a ignora.
Mais detalhes no jira: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1425
[...] primeiro post neste blog gerou algumas duvidas que rendem ate hoje alguns emails sobre o porque de se popular [...]
Daniel, sou chilena e estou trabalhando numa tradução de informática onde encontrei a frase “popule as classes importadas no item anterior…” tenho uma dúbida que não posso esclarecer respeito do termo “popular”….Você podería me ajudar nisto????, a verdade é que estou muito complicada….
agradeço você não ignorar meu questionamento…..
obrigada demais,
Karen
É possível além de definir apenas as colunas que serão retornadas, adicionar critérios como ordenaçao?
Quando tento definir sempre acontece a exception:
ERROR JDBCExceptionReporter:78 - ERROR: column “y0_” does not exist
20:11:24,453 ERROR VisitanteCtr:63 - org.hibernate.exception.SQLGrammarException: could not execute query
org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
Sergio,
É possivel sim. Você pode utilizar todos os recurso que utilizaria com criteria se não estivesse utilizando projection.
Porem para isso você precisa definir alias para as suas entidades, o que de qualquer forma e uma boa pratica.
Ex:
.createCriteria(Produto.class, “p”)
.addOrder( Order.asc(”p.nome”) )
Daniel, como fazer no caso de eu precisar de um atributo de um objeto que esta dentro da minha classe pesquisa? Ex.: preciso mostrar somente o nome das empresas cadastradas no sistema. Tenho a entidade Empresa que possue PessoaJuridica e esta possui o atributo nome. Estou utilizando o AliasToBeanResultTransformer mas esta dando uma excecao de PropertyNotFoundException.
Eis o código (sei que algo está errado, mas ainda nao encontrei a forma correta):
//cria a criteria root da minha query
Criteria criteria = session.createCriteria(Empresa.class,”empresa”);
//cria os alias necessarios para buscar dos campos desejados
criteria.createAlias(”pessoaJuridica”, “retorno1″);
//adiciona na projeção apenas os fields indicados no property,
//sendo que o segundo argumento utiliza reflection para buscar a propriedade desejada.
criteria.setProjection(Projections.projectionList().add(
Projections.property(”empresa.id”),”id”).add(
Projections.property(”empresa.versao”),”versao”).add(
Projections.property(”retorno1.nome”),”nome”));
Primeiro deveriamos saber se o PropertNotFoundException esta se referindo a uma propriedade da projeção que não está sendo encontrado ou da classe que está recebendo essa projeção ( Um resumo do stacktrace ajudaria ).
De qualquer forma adicione o alias da empresa antes da criação do alias da pessoaJuridica
criteria.createAlias(”empresa.pessoaJuridica”, “retorno1″);
Adicionar comentário
Daniel, o hibernate vai gerar um “SELECT * FROM …” ?
Parabéns pelo post.
Nao! Ele vai gerar um select apenas com os atributos que foram projetados. Projections.property(”atributo”)
[...] Muitas pessoas reclamam que perdem o poder do SQL ao utilizar um mapeamento objeto-relacional como o Hibernate. Daniel Passos mostra que existem modos de “recuperar” este “poder”, caso necessário, claro. [...]
Muito legal, resta lembrar que isso é para casos excepcionais…MUITO excepcionais
[]s
Essa feature é muito boa!
Infelizmente ainda tenho que usar Hibernate 2 e várias vezes tive que fazer um session.connection() e trabalhar com o bom e velho JDBC por questões de performace, principalmente em updates
No Hibernate 3 acredito que praticamente todos os problemas desse tipo foram resolvidos ![]()
Dá para fazer isso via HQL também:
1. Primeiro, vc cria um construtor para receber exatamente os dados retornados:
public Produto(Long id, String nome) {
this.id = id;
this.nome = nome;
}
2. Depois usa esse construtor em uma HQL via “new”:
select new Produto(pd.id, p.nome) from Produto p where …
Realmente muito poderoso.
valeuz…
Se a sua classe Produto.class tivesse uma Collection, como fazer para que o setResultTransformer coloque ou a referencia do proxy da coleção em cada instancia de produto, ou já coloque a coleção instanciada ?
Valeu
Simplesmente maravilhoso!!!
Parabens!!!
Marcos,
Ate onde eu sei sempre que se utilizar uma projeção o hibernate ignora as associações que resultam mapeadas como uma coleção. Se vc prestar atenção na query gerada ou debugar o codigo, vera que ele ate seta a coleção para ser projetada mas a ignora.
Mais detalhes no jira: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1425
[...] primeiro post neste blog gerou algumas duvidas que rendem ate hoje alguns emails sobre o porque de se popular [...]
Daniel, sou chilena e estou trabalhando numa tradução de informática onde encontrei a frase “popule as classes importadas no item anterior…” tenho uma dúbida que não posso esclarecer respeito do termo “popular”….Você podería me ajudar nisto????, a verdade é que estou muito complicada….
agradeço você não ignorar meu questionamento…..
obrigada demais,
Karen
É possível além de definir apenas as colunas que serão retornadas, adicionar critérios como ordenaçao?
Quando tento definir sempre acontece a exception:
ERROR JDBCExceptionReporter:78 - ERROR: column “y0_” does not exist
20:11:24,453 ERROR VisitanteCtr:63 - org.hibernate.exception.SQLGrammarException: could not execute query
org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
Sergio,
É possivel sim. Você pode utilizar todos os recurso que utilizaria com criteria se não estivesse utilizando projection.
Porem para isso você precisa definir alias para as suas entidades, o que de qualquer forma e uma boa pratica.
Ex:
.createCriteria(Produto.class, “p”)
.addOrder( Order.asc(”p.nome”) )
Daniel, como fazer no caso de eu precisar de um atributo de um objeto que esta dentro da minha classe pesquisa? Ex.: preciso mostrar somente o nome das empresas cadastradas no sistema. Tenho a entidade Empresa que possue PessoaJuridica e esta possui o atributo nome. Estou utilizando o AliasToBeanResultTransformer mas esta dando uma excecao de PropertyNotFoundException.
Eis o código (sei que algo está errado, mas ainda nao encontrei a forma correta):
//cria a criteria root da minha query
Criteria criteria = session.createCriteria(Empresa.class,”empresa”);
//cria os alias necessarios para buscar dos campos desejados
criteria.createAlias(”pessoaJuridica”, “retorno1″);
//adiciona na projeção apenas os fields indicados no property,
//sendo que o segundo argumento utiliza reflection para buscar a propriedade desejada.
criteria.setProjection(Projections.projectionList().add(
Projections.property(”empresa.id”),”id”).add(
Projections.property(”empresa.versao”),”versao”).add(
Projections.property(”retorno1.nome”),”nome”));
Primeiro deveriamos saber se o PropertNotFoundException esta se referindo a uma propriedade da projeção que não está sendo encontrado ou da classe que está recebendo essa projeção ( Um resumo do stacktrace ajudaria ).
De qualquer forma adicione o alias da empresa antes da criação do alias da pessoaJuridica
criteria.createAlias(”empresa.pessoaJuridica”, “retorno1″);

