Tipos de herança do Hibernate

Quem desenvolve em Java com certeza já ouviu, e muito, que devemos priorizar o desenvolvimento baseado em interfaces no lugar de herança a fim de diminuir o acoplamento do código e torná-lo mais maleável. Porém, nem sempre conseguimos seguir à risca essa recomendação e entramos na tão famigerada herança. Nos últimos tempos tenho percebido no ambiente de trabalho que muitos desenvolvedores se sentem perdidos quando o assunto é persistência de dados utilizando classes derivadas. O artigo de hoje vem para abrir um caminho de luz na sua vida, meu amigo, e tirar essa rusga que existe entre você, a JPA e as heranças. Veremos de forma simplifica, sem entrar em conceitos coadjuvantes e o melhor, hands-on!!!!!


IMPORTANTE!!!!!!

Dependendo da forma de implementação deste recurso e complexidade do projeto, mapear herança na JPA pode ser extremamente oneroso para a aplicação, causando problemas sérios de desempenho. Por isso, seja sapiente ao utilizar esta abordagem 🙂


Antes de iniciar essa peregrinação, vou assumir que você tem os seguintes conhecimentos:

  • Experiência básica em desenvolvimento de aplicações utilizando Java
  • Conhecimento básico da especificação JPA
  • Conhecimento básico da implementação Hibernate
  • Saiba configurar o arquivo persistence.xml

Desta forma, vou ignorar todos os passos básicos de configuração de projeto e bibliotecas e partir para o que interessa. Vamos lá!

Quando tratamos o conceito de herança na JPA, somos levados a três formas distintas de tratamento: o famoso tabelão, tabelas de subclasses e tabelas de classes concretas. É importante salientar que não existe uma receita de bolo dizendo qual é a melhor abordagem, pois cada caso é um caso. Veremos sobre estas abordagens a seguir por meio das classes Pessoa, PessoaFisica e PessoaJuridica.

Tabela única por hierarquia de classes

Este é o famoso tabelão! Nesta abordagem a JPA identifica a classe pai que determina a estratégia do mapeamento da herança e cria uma tabela única contendo todos os dados da super classe e suas classes derivadas. Para identificar quem é quem, é necessário especificar um discriminador para cada classe envolvida na estrutura hierárquica da herança, tornando o processo de persistência eficiente para a JPA. Além disto, será necessário criar um campo a mais na super classe para atuar como o discriminador do processo todo, ou seja, dar nome aos bois 😉

Listagem 01 – implementação da classe Pessoa

O que verdadeiramente importa na classe acima são as considerações abaixo:

  • @Inheritence(strategy = InheritenceType.SINGLE_TABLE): define que utilizaremos o mapeamento de heranças no forma de tabela única
  • @DiscriminatorColum(name = “tipo”, length = 1, discrimanatorType = DiscriminatorType.STRING): informamos que as classes da estrutura serão tipadas por meio da coluna “tipo”, e que o tipo do discriminador será uma String de tamanho 1. O discriminador poderia ser do tipo char ou inteiro.
  • @DiscriminatorValue(“P”): define que os registros oriundos da classe Pessoa serão classificados com o tipo “P”.

Agora vejamos a implementação das classes PessoaFisica e PessoaJuridica, ambas muito parecidas.

Listagem 2 – implementação da classe PessoaFisica

Definimos então que os registros oriundos da classe PessoaFisica serão tipados com “F”.

Listagem 3 – implementação da classe PessoaJuridica

Por fim, determinamos que a classe PessoaJurídica será representada pelo tipo “J”.

Ao executarmos o nosso exemplo e inserirmos alguns dados, será gerado no banco de dados a tabela “pessoa” com os seguintes registros:

Listagem 4 – tabela gerada pela estratégia de mapeamento SINGLE_TABLE

 

 

 

Veja que os registros são classificados como “F” e “J”. Não teremos registros do tipo “P”, pois definimos que a classe Pessoa é abstrata!


É necessário muita atenção ao obter registros desta tabela, pois digamos que a pesquisa seja baseada no ID do registros. Como a super classe é do tipo Pessoa, ela não terá acesso aos atributos CPF e CNPJ, ainda que o registro possua discriminador. Neste caso será necessário verificar o tipo do dado para então manipular os atributos específicos. Pesquisas customizadas por atributos específicos das subclasses podem minimizar este trabalho!


Tabelas por subclasses

Esta abordagem faz com que cada classe da estrutura de herança tenha sua respectiva tabela no banco de dados, contendo os dados específicos da classe mapeada. Com isso, se faz necessário que todas as tabelas das classes derivadas tenham uma chave estrangeira apontando para a chave primária da super classe. Vejamos a implementação desta abordagem a seguir tendo como exemplo as classes Pessoa, PessoaFisica e PessoaJuridica.

Listagem 5 – implementação da classe Pessoa

Dois pontos importantes podem ser observados nesta classe:

  • Remoção do atributo tipo, pois não se faz necessário descriminar o tipo de registro no banco.
  • A estratégia de herança agora é do tipo JOINED, definindo assim que todas as classes derivadas deverão implementar uma chame estrangeira apontando para a chame primária de Pessoa, no caso o atributo “id”.

Vejamos a implementação das classes PessoaFisica e PessoaJuridica.

Listagem 6 – implementação da classe PessoaFisica

O ponto importante desta classe é a definição do @PrimaryKeyJoinColumn(name = “id”), onde o “id” é a chave primária da super classe. Poderia ser qualquer outro nome de sua preferência.

A classe PessoaJuridica implementará a mesma solução da PessoaFisica, vejamos:

Listagem 7 – implementação da classe PessoaJuridica

Veja a seguir as tabelas criadas no banco de dados e a estrutura de registros:

Listagem 8 – tabelas criadas pela estratégia JOINED

 

 

Listagem 9 – registros inseridos

 

 

 

Tabela por classes concretas

Esta abordagem é a mais simples, mas não menos importante. Ela cria uma tabela para cada classe concreta envolvida na estrutura de herança e não necessita de configurações extras nas classes derivadas. Vejamos a implementação desta abordagem.

Listagem 10 – implementação da classe Pessoa

O tipo de estratégia desta abordagem passa a ser TABLE_PER_CLASS. Sugestivo, não?!

As classes derivadas bastam ser anotadas como uma entidade JPA. Vejamos:

Listagem 11 – implementação das classes PessoaFisica e PessoaJuridica

Listagem 12 – tabelas criadas pela estratégia TABLE_PER_CLASS

 


É de extrema importância salientar que a abordagem TABLE_PER_CLASS não suporta estratégia de geração de id’s como AUTO ou IDENTITY, veja a documentação.

2.2.4.1. Table per class

This strategy has many drawbacks (esp. with polymorphic queries and associations) explained in the JPA spec, the Hibernate reference documentation, Hibernate in Action, and many other places. Hibernate work around most of them implementing this strategy using SQL UNION queries. It is commonly used for the top level of an inheritance hierarchy:

This strategy supports one-to-many associations provided that they are bidirectional. This strategy does not support the IDENTITY generator strategy: the id has to be shared across several tables. Consequently, when using this strategy, you should not use AUTO nor IDENTITY.


Espero que tenham gostado. Até a próxima.

Written by Raphael Oliveira Neves
Engenheiro de software, evangelista de novas tecnologias e apaixonado por arquitetura e desenvolvimento de software utilizando Java.