JPA com Hibernate: Herança e Mapeamentos
Postado em 04. mar, 2007 por Paulo Silveira em Java
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.
Paulo Silveira (Google+)
Mais sobre o autor
17 Respostas para “JPA com Hibernate: Herança e Mapeamentos”
Trackbacks/Pingbacks
-
-
janeiro 5, 2008
[...] 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 [...]
-
-
outubro 13, 2009
[...] 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 [...]
-
-
fevereiro 4, 2011
[...] 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-25 trata bastante de [...]
ASSINE NOSSO RSS
Rafael de F. Ferreira
04. mar, 2007
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?
Rafael de F. Ferreira
04. mar, 2007
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>
Paulo Silveira
04. mar, 2007
Oi Rafael
Voce tem razao, devolve
. Ja alterei no artigo, obrigado!
List<AlgumBean>, devolverList<AlgumBean[]>foi resultado do meu copy and pastePaulada
05. mar, 2007
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.
Paulo Silveira
05. mar, 2007
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.
Paulo Silveira
06. mar, 2007
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.
Rafael Brugnollo
27. jun, 2007
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?!?
MARCELLO RIBEIRO
12. jul, 2007
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?
Ricardo Azevedo
26. out, 2007
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,
Leandro Guimarães Faria Corcete DUTRA
04. abr, 2008
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.
Uma duvida
24. out, 2009
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?
vitor
15. mar, 2011
seguinte: uma dúvida…
tenho uma classe Pessoa, por exemplo, e essa classe possui uma lista de endereços. tenho muitos campos na classe pessoa que não são necessários para um relatório e eu não queria trazer eles em um select, mas preciso dos endereços.
consigo criar um VO que receba no construtor por exemplo
(string nome, string sobrenome, List enderecos) e na query fazer
“select new valueObj(pessoa.nome, pessoa.sobrenome,pessoa.enderecos) from …. ???
Carlos Amaral
25. jul, 2011
Mas a normalização é obrigatória e necessária para não haver redundância de dados.
Exemplo : Tabela Pessoa (id, nome, apelido, cnpj, tipo)
Todas as Pessoas do aplicativo tem um tipo : alunos, fornecedores, professores, agencias bancarias, sindicatos, escritorios de contabilidade, etc, estão nesta tabela e eu quero separa-las conforme o caso de uso.
No caso de agencia bancária, a única diferença é que tenho que guardar o número da agencia e o banco (tabela Banco).
Haveria aí uma tabela AgenciaBancaria(idPessoa, idBanco, numeroAgencia)
Como proceder neste caso ? Terei 50.000 Pessoas sendo que, no máximo, 5 agencias bancárias. Vou modelar a minha tabela Pessoa com o campo numeroAgencia e deixar 49.995 registros com este campo em branco ?
Aí é necessário esta composição, mas também é complicado fazer a união citada, pois para cada tipo haverá uma tabela com a particularidade daquele tipo.
Como proceder ?
Halley
06. mar, 2013
Olá,
Tenho uma dúvida sobre o mapeamento Join no seguinte cenário. Pessoa e Funcionário.
Ao utilizar Join, o iden de Funcionário será o mesmo iden de Pessoa. Mas tenho a seguinte regra: Toda vez que o funcionário possui um novo cadastro na empresa (vamos imaginar que ele entrou na empresa, saiu e voltou) , teria então dois registros do funcionário, mas somente um registro de Pessoa.
Como ficaria isso, tenho uma única pessoa vinculada a duas filhas? Gostaria de utilizar a estrutura da herança, mas sem a necessidade de ter uma relação 1 para 1 com a classe superior. (E manter a normalização)