Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


JPA com Hibernate: Herança e Mapeamentos

Por Paulo Silveira em 04/03/07

Essas semanas postarei algumas dicas rápidas no uso da JPA com Hibernate. São alguns pontos que sempre aparecem no desenvolvimento, referentes a performance, elegância e facilidades. Alguns tópicos já são de amplo conhecimento dos usuários do Hibernate, porém na JPA alguns deles são utilizados diferentemente.

Cuidado com herança por InheritanceType.JOINED

Aqui na Caelum, conforme discutido anteriormente, usamos herança com muito critério. Herança na JPA é mapeada com @Inheritance(strategy=InheritanceType.SINGLE_TABLE) por padrão, isto é, ele vai utilizar uma única tabela para guardar todos os dados de todas as classes filhas: não há normalização e uma coluna (o discriminator, por default DTYPE no Hibernate) será utilizada para distinguir entre as possíveis subclasses.

Muitos administradores de banco de dados reclamam dessa estratégia, sendo que a mais elegante é a InheritanceType.JOINED, onde cada classe terá uma tabela, mas sem repetir colunas. As tabelas que representam as classes filhas possuem uma chave estrangeira para a tabela que representa a mãe, normalizando o banco nesse aspecto.

O grande problema dessa estratégia são as queries polimórficas: no caso de você possuir uma classe mãe Pessoa e duas filhas PessoaFisica e PessoaJuridica, ao procurar por uma Pessoa pela sua chave primária o hibernate vai gerar um join entre todas essas tabelas (pode variar um pouco de acordo com o dialeto usado), já que não possuímos o discriminator nesse caso.

Mapear resultados em vez de trabalhar com List<Object[]>

Se executamos uma Query em que nosso select não escolhe apenas um tipo de valor a ser retornado, recebemos uma List<Object[]> como resultado. Por exemplo, um select e1.atributoInteiro, e2.atributoString from Entidade1 as e1, Entidade2 as e2 where... retornaria uma List<Object[]> em que, para cada item da lista, a primeira posição da array é um Integer referente ao atributoInteiro da Entidade1 e a segunda posição é uma String referente ao atributoString da Entidade2.

Tanto a JPA quanto o Hibernate permitem retornar qualquer tipo de objeto através da clausula de select, utilizando um construtor. Podemos mudar a query anterior para select new br.com.caelum.Bean(e1.atributoInteiro, e2.atributoString) from Entidade1 as e1, Entidade2 as e2 where... para receber um List<AlgumBean> como resultado, desde que a classe br.com.caelum.AlgumBean possua um construtor que receba um Integer e uma String. Muito mais elegante que precisar percorrer uma List<Object[]> e encher nosso código de castings. Excelente parar gerar relatórios e já devolver os dados organizados em beans específicos.

Para mapeamentos mais avançados o Hibernate possui a interface ResultTransformer e sua factory Transformers. A JPA define a SQLResultSetMapping e outras anotações, mas para o uso de native queries.

  • Share/Bookmark

13 Comments »

  1. Como de costume, muito bom este post.
    Eu soh nao entendi um negocio, o resultado da query com construtor eh mesmo List ou eh List?

    Comment by Rafael de F. Ferreira — March 4, 2007 @ 1:12 pm

  2. O wordpress comeu os (menor|maior)-que… Repetindo:

    Como de costume, muito bom este post.
    Eu soh nao entendi um negocio, o resultado da query com construtor eh mesmo List<AlgumBean[]> ou eh List<AlgumBean>

    Comment by Rafael de F. Ferreira — March 4, 2007 @ 1:14 pm

  3. Oi Rafael

    Voce tem razao, devolve List<AlgumBean>, devolver List<AlgumBean[]> foi resultado do meu copy and paste :) . Ja alterei no artigo, obrigado!

    Comment by Paulo Silveira — March 4, 2007 @ 1:46 pm

  4. Olá,

    Me corrija se eu estiver errado, mas pelo que andei lendo de mapeamento de herança na JPA, TABLE_PER_CLASS é: cada classe é mapeada em uma tabela separada e não em uma única tabela com uma coluna discriminator como você disse.
    SINGLE_TABLE é a estratégia de uma única tabela.

    Valeu.
    Paulo.

    Comment by Paulada — March 5, 2007 @ 7:25 am

  5. Ola Paulo

    Você está absolutamente correto. Era para colocar SINGLE_TABLE, que alias é o default que falei. Quem sabe depois de mais algumas correções esse post fica bom :) .

    Ja mudei, obrigado.

    Comment by Paulo Silveira — March 5, 2007 @ 7:53 am

  6. Só para ficar registrado: um atual cliente estava usando JOIN_TABLE, e como a mae tinha 6 filhas estava gerando um join que excedia 32767 caracteres! Incrivel! A solucao rapida é passar para SINGLE_TABLE, a solucao bonita eh passar para relociamento, e trocar heranca por composicao.

    Comment by Paulo Silveira — March 6, 2007 @ 3:49 pm

  7. Estou querendo aprender sobre o Hibernate e já li as apostilas da Caelum…. alguem tem um bom material(em português) para me indicar para eu estudar?!?

    Comment by Rafael Brugnollo — June 27, 2007 @ 6:34 pm

  8. Pessoal, aproveitando a proficiência de vcs em JPA… Alguém saberia me dizer porque em algumas consultas o JPA tentar retirar campos do tipo varchar do resultset usando getDouble e ai exceção de número inválido?

    Comment by MARCELLO RIBEIRO — July 12, 2007 @ 8:22 pm

  9. Olá Paulo,

    Obrigado pelo post, ajudou muito em nossos testes de JPA.

    Estamos fazendo testes com Herança com JPA e surgiu uma dúvida sobre o mapeamento de Herança com sobreposição (overlapping) , onde eu possa ter mais de uma subclasse para uma mesma classe pai. Exemplo: Pessoa (pai) que pode ser um Cliente (filha), mas também um Fornecedor (filha), ou um Funcionário (filha) que também pode ser um Vendedor (filha).

    Problema: Quando tentamos mapear isto com o relacionamento joined-class, ao criar o segundo registro filho, ocorre um erro, pois ele tenta criar novamente o registro pai.

    Uma solução que me passaram seria forçar a exclusão do pai, antes de incluir um novo filho, mas existem problemas de restrição no próprio banco.

    Pergunta: Existe alguma anotação específica para determinar esta propriedade da herança em UML (overlapping) ou existe uma forma melhor de mapear esta herança com o Hibernate?

    Obrigado e um abraço,

    Comment by Ricardo Azevedo — October 26, 2007 @ 9:40 am

  10. [...] em relação à burocracia do EJB2; a nova API de Persistência Java (JPA) (veja [1], [2], [3], [4]) — que também tira muito proveito das anotações e simplifica a persistência dos Entity [...]

    Pingback by Blog do Márcio d’Ávila » Boas blogadas - novidades e atualidades Java EE 5/6 — January 5, 2008 @ 1:57 am

  11. O problema é que (1) não de podem mapear classes e tabelas, (2) o modelo de dados tem de ser criado indepentemente do de classes, porque o ponto fulcral do sistema é a base de dados, não o programa aplicativo.

    Comment by Leandro Guimarães Faria Corcete DUTRA — April 4, 2008 @ 3:48 pm

  12. [...] sobre os estados de uma entidade na JPA, hábitos importantes para todo desenvolvedor Hibernate e mapeamento de herança, além de muitos outros artigos relacionados ao framework. O nosso curso FJ-26 trata bastante de [...]

    Pingback by Enfrentando a LazyInitializationException no Hibernate | blog.caelum.com.br — October 13, 2009 @ 1:52 pm

  13. Imagina que eu tenho Classe Pessoa,PessoaFisica,PessoaJuridica,Cliente

    Como seria esta relação?Como fazer o JPA entender que quando gravar em Cliente,deve-se gravar em Pessoa e dependendo da opcao , gravar ou em pessoa juridica ou em pessoa fisica?

    Comment by Uma duvida — October 24, 2009 @ 5:21 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment




Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222