Implementando Herança com Hibernate

E ai galera tudo bom?

Dando um tempo nos posts sobre Criteria com Hibernate vamos mudar de tema para descontrair…

Hoje o tema é Herança com Hibernate, na qual vejo que muitas pessoas tem certa dificuldade em implementar e saber qual forma utilizar.

Nos exemplos deste post irei utilizar classes bem simples, uma para o post não ficar muito cansativo e outra para ficar de fácil entendimento. Em todos os exemplos irei utilizar o tão famoso caso da Pessoa Física e Pessoa Jurídica que tem muitos atributos em comum.

Antes de sair implementando igual um louco devemos entender se isso é realmente viável e qual a melhor forma de implementar. Realizar a implementação da Herança às vezes pode ser inviável e pode ser facilmente trocada por composição na qual facilita na manutenção do código e diminui o acoplamento entre as classes.

O Hibernate nos fornece várias estratégias possíveis para mapeamento utilizando Herança. São elas:

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

Let’s Go.

Tabela Única por Hierarquias de Classes

Essa estratégia de mapeamento define que seja gerada apenas uma tabela para todas as hierarquias de classe. A superclasse irá conter os dados comuns e as subclasses devem possuir um discriminador único.

A implementação

A 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
}

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
Define qual a estratégia de mapeamento de Herança será utilizado

@DiscriminatorColumn(name = “tipo”, length = 1, discriminatorType = DiscriminatorType.STRING)
Define qual atributo será identificador da classe mapeada
name: Define o nome do atributo identificador na tabela
length: Define o tamanho da coluna identificadora
discriminatorType: Define qual o tipo de valor que irá identificar as classes

@DiscriminatorValue(“P”)
Define o valor identificador da Classe Pessoa

Aqui vai uma observação muito importante, repare no atributo tipo da classe Pessoa, ele deve possuir o valor dos atributos insertable e updatable definidos como false, caso contrário o Hibernate não conseguirá realizar a criação das tabelas no banco de dados.

A Classe PessoaFisica

@Entity
@DiscriminatorValue(value = "F")
public class PessoaFisica extends Pessoa {

    private String cpf;

    //getters and setters omitidos
}

@DiscriminatorValue(value = “F”)
Define o valor identificador da Classe PessoaFisica

A Classe PessoaJuridica

@Entity
@DiscriminatorValue(value = "J")
public class PessoaJuridica extends Pessoa {

    private String cnpj;

    //getters and setters omitidos
}

@DiscriminatorValue(value = “J”)
Define o valor identificador da Classe PessoaJuridica

Vamos ver agora o resultado da nossa primeira implementação

Tabela Única por Hierarquias de Classes

Resultado da implementação de tabela única por hierarquias de classes

Tabela por Subclasse

Essa estratégia de mapeamento define que uma subclasse pode ser mapeada para sua própria tabela. Essa estratégia não exige um discriminador, no entanto, a subclasse possui um atributo identificador que é o mesmo utilizado na superclasse. No caso dessa estratégia de mapeamento é utilizado como identificador a chave primária (PK) da superclasse tornando-a uma chave primária/chave estrangeira (PK/FK) nas subclasses.

A implementação

Classe 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
}

@Inheritance(strategy = InheritanceType.JOINED)
Define qual a estratégia de mapeamento de Herança será utilizado

Classe PessoaFisica

@Entity
@PrimaryKeyJoinColumn(name="idPessoa")
public class PessoaFisica extends Pessoa {

    private String cpf;

    //getters and setters omitidos
}

@PrimaryKeyJoinColumn(name=”idPessoa”)
Define qual será o atributo identificador da tabela

Observamos que no mapeamento dessa classe não utilizamos a anotação @GeneratedValue pois o Hibernate se encarrega de gerar os valores, caso você utilize a anotação não irá conseguir efetuar a criação das tabelas no banco de dados pelo Hibernate.

Classe PessoaJuridica

@Entity
@PrimaryKeyJoinColumn(name="idPessoa")
public class PessoaJuridica extends Pessoa {

    private String cnpj;

    //getters and setters omitidos
}

@PrimaryKeyJoinColumn(name=”idPessoa”)
Define qual será o atributo identificador da tabela

Vamos ver agora o resultado da nossa segunda implementação

Tabela por Subclasse

Resultado da implementação de tabela por subclasse

Tabela por Classe Concreta

Essa estratégia de mapeamento define que seja gerada uma tabela por classe concreta. Cada tabela possui seus atributos, incluindo os atributos herdados da superclasse.

A implementação

Classe 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
}

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
Define qual a estratégia de mapeamento de Herança será utilizado

Um ponto muito importante a ser observado é a utilização da keyword abstract, caso não seja utilizada, na hora da criação das tabelas o Hibernate irá criar uma nova tabela contendo apenas os atributos da classe Pessoa.

Classe PessoaFisica

@Entity
public class PessoaFisica extends Pessoa {

    private String cpf;

    //getters and setters omitidos
}

Classe PessoaJuridica

@Entity
public class PessoaJuridica extends Pessoa {

    private String cnpj;

    //getters and setters omitidos
}

Vamos ver agora o resultado da nossa terceira implementação

Resultado da implementação de tabela por classe concreta

Resultado da implementação de tabela por classe concreta

Herdar propriedades de Superclasses

Comumente é útil compartilhar atributos para diversas entidades ou tabelas do banco de dados, por meio de uma técnica presente no Hibernate conseguimos fazer isso de forma prática e fácil. Para realizar esse procedimento basta utilizar a anotação @MappedSuperClass, na qual a anotação pode estar tanto na subclasse quanto na superclasse.

A implementação

Classe Pessoa

@MappedSuperclass
public class Pessoa implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    @Version
    private Timestamp versao;

    //getters and setters omitidos
}

@MappedSuperClass
Define que os atributos pertencentes a está classe irão ser mapeados por outra classe, ou seja, nesse caso por uma subclasse.

Classe PessoaFisica

@Entity
public class PessoaFisica extends Pessoa {

    private String cpf;

    //getters and setters omitidos
}

Classe PessoaJuridica

@Entity
public class PessoaJuridica extends Pessoa {

    private String cnpj;

    //getters and setters omitidos
}

Vamos ver agora o resultado da nossa quarta e última implementação

Resultado da implementação da herança de propriedades

Resultado da implementação da herança de propriedades

Bom, como dito no inicio do post devemos verificar a viabilidade de utilizar Herança, principalmente quando sua utilização está presente em classes persistentes. Com este post você pode ver qual tipo de implementação de Herança atende a sua necessidade ou pode simplesmente abandonar a Herança e começar a utilizar composição, que em minha opinião é a forma mais adequada.

Bom galera, espero ter sido claro nas explicações e até o próximo post.

Abraços a todos.

Fonte de pesquisa
Documentação do Hibernate: http://www.hibernate.org/docs

Tags: , , , , , , , , ,

11 Respostas to “Implementando Herança com Hibernate”

  1. www.diegoporfirio.com Says:

    Cara, muito bom mesmo. Parabéns.

  2. Douglas Ribeiro Says:

    Exatamente oque eu estava procurando, explicou de uma forma bem simples e que foi bem fácil compreender !
    Valeu!!!!

  3. Rafael Rossi Says:

    Irmão, show de bola esse exemplo seu. Para eu que estou começando a estudar Java/Hibernate isso ai ficou extremamente facil de entender e ajudou muito.
    Obrigado.

  4. Conrado Says:

    Bom dia, fiz a primeira implementação com o tipo, nesse caso em meu controller o que faria no ação novo e salvar?
    Estou fazendo um tela somente, e estou utilizando o tipo em um combo passando para ele F em caso de fisica e FO em caso de fornecedor(que seria meu juridica).

    • Altieres Says:

      Bom dia Conrado,
      desculpe a demora para responder, mais é que meus dias estão bem ocupados!
      Basta verificar o Combo, se ele estiver com F você salva dao.salvar(fisica) e caso ele esteja FO você salva dao.salvar(juridica).
      É apenas um exemplo. Caso precise de mais ajuda pode me enviar um e-mail.
      Abraços!

  5. Daniel Says:

    Tanto no “Tabela por Subclasse” e “Tabela por Classe Concreta”
    as classes filhas nao podem ter primary key? se eu precisar ter as PK na filhas, como eu faço a herança? obg.

  6. Bruno Says:

    Muito legal, didático e funcional. Parabéns Altieres…

  7. Thiago Says:

    Boa noite… Programador thiago , gostaria de Saber , meu caro , como faço para listar os dados numa AbstractTableModel em relação a persistencia JPA … existe um método para que a JPA possa carregar na Tabela os meus dados ? Abraços. 🙂 Muito bom o seu post. Copiei pra minha pessoa e graças ao mesmo , já fiz vários exemplos com Persistencia , JPA no NetBeans e SQL Server. valeu 🙂

  8. Petros Schütz Schilling Says:

    Muito bom! Estava gerando tudo com a coluna type (que não gosto muito). Excelente explicação.

  9. Thiago Marques Silva Says:

    Muito útil! Sucinto, claro, preciso nas explicações. Parabéns!

  10. Daniel Paulino Says:

    Valeu irmão, PARABÉNS!! Bem explicado e objetivo, valeu!

Deixar mensagem para Douglas Ribeiro Cancelar resposta