Quando se trata de herança o Hibernate trata deste assunto muito bem e com eficácia, diga-se de passagem. Ocorre que não há apenas 1 método para realizar a Herança de classes no Hibernate, o que confunde a maioria dos desenvolvedores. Antes de dar início é importante salientar que o uso inapropriado de herança no Hibernate pode causar uma lentidão incalculável na sua aplicação, por isso pense se a melhor alternativa é composição ou herança.

Enfim, nosso foco não é mostrar se o melhor é composição ou herança, mas sim as formas de se implementar uma herança no Hibernate. Estas formas são 3:

  • Tabela Única por Hierarquia de Classes
  • Tabela por Subclasse
  • Tabela por Classe Concreta

Cada uma dessas tem sua peculiaridade, e não podemos simplesmente dizer: A solução X é melhor que Y, pois isso depende de inúmeras variáveis, tais como: suas regras de negócio.

Tabela Única por Hierarquia de Classes

Este é o famoso “tabelão” com milhares de campos, onde é gerado 1 tabela para toda a hierarquia de classes. Se você tiver uma Classe Pessoa como Superclasse, e outras 2 classes PessoaFisica e PessoaJuridica estendendo de Pessoa, o Hibernate irá gerar apenas 1 tabela Pessoa com os dados de Pessoa + PessoaFisica + PessoaJuridica.

E como é feita a divisão de quem é Pessoa Física e quem é Pessoa Jurídica ? Através de um atributo chamado discriminator. No seu “tabelão” deverá ter um campo que faça a discriminação entre PessoaFisica (F) e PessoaJuridica (J). Veja abaixo na listagem 1 a implementação da classe Pessoa.

Listagem 1: mplementação da Classe Pessoa


@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "tipo", length = 1, discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("P")
public class Pessoa implements Serializable {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idPessoa;
    private String nome;
    @Column(insertable=false, updatable=false)
    private String tipo;
 
    //getters and setters omitidos
}

O que vai nos importar no código acima são 3 linhas:

  1. @Inheritance(strategy = InheritanceType.SINGLE_TABLE): Identifica que a estratégia de herança será de uma única tabela, ou se preferir, um “tabelão” para tudo.
  2. @DiscriminatorColumn(name = "tipo", length = 1, discriminatorType = DiscriminatorType.STRING): Identifica que o campo que armazenará o discriminator é o “tipo”, de tamanho 1, do tipo String.
  3. @DiscriminatorValue("P"): Identifica que a Classe Pessoa será identifica com o atributo “P” na tabela Pessoa, ou seja, onde tiver “P” é porque aquele registro é uma pessoa.

Vamos ver agora na listagem 2 a implementação da pessoa física:

Listagem 2: Implementação da Pessoa Física


@Entity
@DiscriminatorValue(value = "F")
public class PessoaFisica extends Pessoa {
 
    private String cpf;
 
    //getters and setters omitidos
}

Bem simples, o que nos interessa saber é que seu DiscriminatorValue é “F”, ou seja, onde tiver um “F” no campo tipo da tabela, significa que é uma Pessoa Física.

Listagem 3: Implementação da Pessoa Jurídica


@Entity
@DiscriminatorValue(value = "J")
public class PessoaJuridica extends Pessoa {
 
    private String cnpj;
 
    //getters and setters omitidos
}

Dispensa explicações, mas é importante ser redundante para fixação do conteúdo. Onde tiver o valor “J” no campo tipo, significa que é uma Pessoa Jurídica.

Na figura 1, veja qual será o resultado da tabela:

Tabela Única por Hierarquia de Classes

Figura 1: Tabela Única por Hierarquia de Classes

Tabela por Subclasse

Nessa estratégia nós teremos a Classe Pai e todas suas filhas geradas no banco de dados fisicamente, sendo que em todas as classes filhas teremos uma chave estrangeira que apontará para a classe pai.

Isto é, se tivermos 1 Classe Pessoa com 5 atributos, 1 Classe Pessoa Física com 10 atributos e 1 Classe Pessoa Jurídica com 15 atributos, o gerado será: 1 tabela pessoa com 5 atributos, 1 tabela pessoa_fisica com 10 atributos e 1 tabela pessoa_juridica com 15 atributos. Vamos a exemplos.

Listagem 4: Implementação da Pessoa


@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Pessoa implements Serializable {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idPessoa;
    private String nome;
 
    //getters and setters omitidos
}

A linha que nos interessa aqui é:

@Inheritance(strategy = InheritanceType.JOINED): Identifica que a estratégia de herança será “JOINED”, ou seja, será feita uma junção através de chaves estrangeiras.

Listagem 5. Implementação PessoaFisica

@Entity
@PrimaryKeyJoinColumn(name="idPessoa")
public class PessoaFisica extends Pessoa {
 
    private String cpf;
 
    //getters and setters omitidos
}

A linha importante aqui é:

@PrimaryKeyJoinColumn(name="idPessoa"): Identifica qual campo fará essa “junção” entre a tabela pessoa_fisica e a tabela pessoa.

Listagem 6: Implementação PessoaJuridica


@Entity
@PrimaryKeyJoinColumn(name="idPessoa")
public class PessoaJuridica extends Pessoa {
 
    private String cnpj;
 
    //getters and setters omitidos
}

Este código acima segue a mesma lógica da Listagem 5, a coluna idPessoa faz a “junção” entre a tabela pessoa e a tabela pessoa_juridica. Veja como ficou as tabelas geradas.

Tabela por Subclasse

Figura 2: Tabela por Subclasse

Tabela por Classe Concreta

Não menos importante, essa estratégia define que apenas as classes concretas geram suas respectivas tabelas, ou seja, em nosso exemplo apenas pessoaFisica e pessoaJuridica criarão tabelas, isso porque pessoa é uma classe abstrata.

Listagem 7: Implementação de Pessoa


@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Pessoa implements Serializable {
 
    @Id
    private Long idPessoa;
    private String nome;
 
    //getters and setters omitidos
}

A nossa estratégia agora é “TABLE_PER_CLASS”, que identifica que apenas as classes concretas serão geradas.

Listagem 8: Implementação PessoaFisica


@Entity
public class PessoaFisica extends Pessoa {
 
    private String cpf;
 
    //getters and setters omitidos
}

Nada de diferente aqui já que a estratégia é TABLE_PER_CLASS.

Listagem 9: Implementação PessoaJuridica


@Entity
public class PessoaJuridica extends Pessoa {
 
    private String cnpj;
 
    //getters and setters omitidos
}

Ainda nada de novo. Enfim, o resultado da sua tabela será visto na figura 3:

Tabela por Classe Concreta

Figura 3: Tabela por Classe Concreta

CONCLUSÃO

Vimos neste artigo 3 tipos de estratégia de herança no Hibernate: SINGLE_TABLE, JOINED e TABLE_PER_CLASS. Cada um com sua peculiaridade e seus prós e contras. É difícil definir qual é o melhor, pois não há melhor, o melhor é você saber definir quando usar cada um destes.