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.

1
2
3
4
5
6
7
8
Collection collection = HibernateSessionFactory.getSession()
.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

  1. Tirso Andrade em 21.nov.06 às 1:39 pm

    Daniel, o hibernate vai gerar um “SELECT * FROM …” ?
    Parabéns pelo post.

  2. Daniel Passos em 21.nov.06 às 6:45 pm

    Nao! Ele vai gerar um select apenas com os atributos que foram projetados. Projections.property(“atributo”)

  3. Phillip Calçado "Shoes" em 25.nov.06 às 2:10 pm

    Muito legal, resta lembrar que isso é para casos excepcionais…MUITO excepcionais ;)

    []s

  4. Rubem Azenha em 26.nov.06 às 2:03 pm

    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 :)

  5. Marcos Silva Pereira em 26.nov.06 às 3:47 pm

    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…

  6. Marcos Urata em 12.dez.06 às 3:38 pm

    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

  7. Alexandre em 14.dez.06 às 7:50 am

    Simplesmente maravilhoso!!!
    Parabens!!!

  8. Daniel Passos em 15.dez.06 às 10:15 am

    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

  9. karen em 12.jul.07 às 2:14 pm

    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

  10. Sergio em 2.ago.07 às 8:12 pm

    É 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)

  11. Daniel Passos em 10.ago.07 às 2:09 pm

    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”) )

  12. Rogerio em 22.out.07 às 10:11 am

    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”));

  13. Daniel Passos em 3.dez.07 às 2:09 pm

    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″);

Trackbacks

  1. Fragmental » Consultas no Hibernate
  2. DClick Blog

Deixe Seu Comentário