Hierárquicas
Introdução
Quando se trabalha com JPA (Java Persistence API), o mapeamento de herança entre entidades pode ser uma das partes mais complexas e, ao mesmo tempo, poderosas no desenvolvimento de sistemas orientados a objetos. A herança no JPA permite que você modele hierarquias de classes, reutilizando atributos e comportamentos em classes filhas, ao mesmo tempo em que controla como esses dados são persistidos no banco de dados.
Neste tutorial, vamos explorar o mapeamento de herança com JPA, utilizando um cenário prático envolvendo duas entidades: Cliente e Fornecedor. Ambas compartilham atributos comuns, como id
, cpfCnpj
e nome
, mas cada uma possui também atributos específicos: o limiteCredito
para o Cliente e a primeiraParcela
para o Fornecedor. A partir deste cenário, vamos abordar três estratégias comuns de mapeamento de herança no JPA: Single Table Inheritance (STI), Joined Table Inheritance (JTI) e Table Per Class Inheritance (TPC).
Cada uma dessas estratégias oferece vantagens e desvantagens, dependendo dos requisitos do seu sistema, como performance, simplicidade de implementação e flexibilidade na modelagem dos dados. Ao Integero deste tutorial, você aprenderá como implementar cada uma dessas abordagens e entenderá em quais situações elas são mais adequadas, ajudando a decidir qual é a melhor estratégia para o seu projeto.
Estratégias
Single Table
Single Table
Nessa estratégia, todas as classes (Cliente e Fornecedor) compartilham uma única tabela no banco de dados. Para diferenciar as entidades, uma coluna extra chamada geralmente discriminator é adicionada. A tabela terá todos os campos de ambas as entidades, incluindo os campos específicos de cada uma.
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "tipo", discriminatorType = DiscriminatorType.STRING)
@Entity
public class Pessoa {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String cpfCnpj;
private String nome;
}
@Entity
@DiscriminatorValue("CLIENTE")
public class Cliente extends Pessoa {
private Double limiteCredito;
}
@Entity
@DiscriminatorValue("FORNECEDOR")
public class Fornecedor extends Pessoa {
private Integer primeiraParcela;
}
Joined Table
Joined
Nessa estratégia, cada classe tem sua própria tabela. A tabela da classe filha contém uma chave estrangeira que referencia a tabela da classe pai. As tabelas para Cliente
e Fornecedor
armazenam apenas seus atributos específicos.
@DiscriminatorValue
nas classes filhas.@Inheritance(strategy = InheritanceType.JOINED)
@Entity
public class Pessoa {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String cpfCnpj;
private String nome;
}
Table Per Class
Table Per Class
Com esta estratégia, cada classe tem sua própria tabela e todas as colunas (inclusive as da classe pai) são duplicadas nas tabelas das classes filhas. Não há chave estrangeira entre as tabelas.
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
public class Pessoa {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
//demais campos
}
Mapped Superclass
Mapped Superclass
A estratégia Mapped Superclass no JPA é uma abordagem usada quando você quer compartilhar atributos e comportamentos entre várias entidades, mas sem mapear uma tabela para a superclasse. Diferente das outras estratégias de herança (como SingleTable, Joined ou TablePerClass), a MappedSuperclass
não cria uma tabela no banco de dados para a superclasse, mas permite que as subclasses herdem seus atributos.
@MappedSuperclass
public class Pessoa {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String cpfCnpj;
private String nome;
// Getters e setters
}
Você Sabia?
A principal diferença entre Table Per Class e Mapped Superclass no JPA está na forma como as tabelas são gerenciadas e como as entidades são persistidas no banco de dados
- Table Per Class cria uma tabela para cada classe, incluindo a superclasse, o que pode levar a redundância de dados.
- Mapped Superclass não cria uma tabela para a superclasse, apenas as subclasses herdam seus atributos, evitando a redundância, mas sem permitir consultas diretas à superclasse.
Conclusão
As estratégias de mapeamento por herança no JPA oferecem diferentes formas de modelar hierarquias de classes, cada uma com suas vantagens e desvantagens, dependendo dos requisitos do sistema.
- Single Table Inheritance (STI) é uma solução simples, onde todas as classes da hierarquia são armazenadas em uma única tabela. Embora seja eficiente em termos de estrutura, pode levar a problemas de redundância e valores nulos para atributos não aplicáveis em todas as entidades.
- Joined Table Inheritance (JTI) promove uma abordagem mais normalizada, com cada classe e seus atributos sendo armazenados em tabelas separadas. Essa estratégia reduz a redundância de dados, mas pode impactar o desempenho devido à necessidade de realizar junções nas consultas.
- Table Per Class Inheritance (TPC), por sua vez, cria uma tabela exclusiva para cada classe, duplicando os dados da superclasse nas tabelas filhas. Embora elimine a necessidade de junções, essa estratégia pode resultar em um maior uso de armazenamento devido à duplicação de dados.
- Mapped Superclass, por outro lado, é uma abordagem que não envolve criação de tabelas para a superclasse, permitindo que as subclasses herdem os atributos e comportamentos comuns sem redundância de dados, mas sem a possibilidade de realizar consultas diretas à superclasse.
A escolha entre essas estratégias deve ser baseada nas necessidades específicas do seu projeto, como performance, redundância de dados, complexidade das consultas e modelo de persistência desejado. Em sistemas mais simples, o Single Table Inheritance pode ser suficiente, enquanto em sistemas maiores, que exigem maior normalização e controle sobre as consultas, o Joined Table Inheritance ou Table Per Class Inheritance podem ser mais adequados. O Mapped Superclass é ideal para cenários onde a superclasse não deve ter uma tabela própria, mas seus atributos precisam ser compartilhados pelas subclasses.
Em última análise, entender as implicações de cada abordagem no desempenho, manutenção e estrutura do banco de dados é essencial para fazer a escolha mais apropriada, garantindo que o mapeamento de herança no JPA atenda às necessidades do sistema de forma eficiente e escalável.