terça-feira, 11 de setembro de 2007

Herança "simples" com Hibernate

Olá pessoas,

Hoje comentarei sobre mapeamento de herança com o Hibernate através de annotations.
Conforme documentações e exemplos que encontramos por ai, sabemos que existem 3 tipos de mapeamento de herança:
  • Joined - Usado quando quer criar uma tabela para a classe genérica e demais tabelas que derivam dela, por exemplo: Pessoa, Pessoa Física, Pessoa Jurídica. Neste caso teriam 3 tabelas.
  • Single Table - Usado quando quer criar uma única tabela para armazenar os dados. No caso da Pessoa, Pessoa Física, Pessoa Jurídica, teria uma única tabela com todas os atributos.
  • Table Per Class - Usado quando quer criar uma tabela para cada subclasse. No caso da Pessoa, Pessoa Física, Pessoa Jurídica, teriam duas tabelas: Pessoa Fisica e Pessoa Juridica, os atributos da classe Pessoa seriam replicados nas duas tabelas.
A questão é que para utilizar estes tipos de mapeamento de herança é necessário anotar a classe mais genérica com @Entiety, ou seja deve ser uma entidade gerenciada pelo hibernate. Se uma classe é uma Entiety, obrigatoriamente ela deve conter um atributo anotado com @Id.
Se for utilizar o mapeamento de herança do tipo Table Per Class, não é possível determinar o atributo anotado com @Id como @GeneratedValue do tipo AUTO ou IDENTITY. Em alguns casos isso é um problema, para explicar melhor vou citar um exemplo:

Uma classe abstrata Modelo possui alguns atributos incluindo um ID.
Uma classe concreta ModeloCorporativo deriva de Modelo e possui mais alguns atributos.
A nível de base de dados, a tabela do modelo corporativo deve possuir todos os atributos da classe modelo e mais os seus atributos. Dentre estes atributos, está o ID, este deve ser gerado automaticamente pelo banco (via sequence, via auto_increment, etc)

A primeira vista, parece que o melhor tipo de mapeamento para esta herança é usar TablePerClass, mas dessa forma o atributo ID da classe abstrata não poderia estar anotado com @GeneratedValue(stategy=GenerationType.AUTO), pois isso é uma regra do hibernate (inclusive citada na documentação do hibernate annotations).
Então para resolver esta situação a forma mais indicada seria criar uma classe abstrata Modelo e anota-la com @MappedSuperclass, Este tipo de mapeamento de herança (que nunca aparece nos exemplos comuns de herança) não exige que a classe seja anotada com @Entiety, ou seja, o hibernate vai saber que esta classe só vai servir para "embutir" os atributos na tabela representada pelas subclasses.

O código ficaria assim:
@MappedSuperclass
public abstract class Modelo {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;

// outros atributos e getters e setters omitidos.
}

@Entity
public class ModeloCorporativo extends Modelo {
private String mascara;

// getters e setters omitidos
}

Desta forma a tabela ModeloCorporativo terá todos os atributos da classe Modelo e mais o seu atributo mascara. O importante é que o atributo ID herdado da classe Modelo pode ser anotado com @GeneratedValue(strategy=GenerationType.AUTO) e, de acordo com o banco de dados, terá seu id gerado automaticamente
Qualquer dúvida posta aí.
[]'s

3 comentários:

Unknown disse...

Olá ,no caso de (InheritanceType.JOINED) ter um mapeamento modelo (superclass)
modelo1 e modelo2 (subclasses, usando discriminator).
Tenho gravado modelo1, e quero alterar para modelo2(sem alterar a superclasse), sabe se isso é possivel?

Germano disse...

Olá amigo, não consegui obter o seu endereço de e-mail ou blog para responder.
Mas caso voltes aí vou responder:
Sim, é possível, e muito simples. Você obtem o objeto referente ao modelo1 (através de um session.load(Modelo1.class, idASerCarregado)) por exemplo e atualiza as informações que queres neste objeto, se alterares o atributo que é o discriminator dessa classe então o hibernate irá transferir o registro que estava na tabela do modelo1 para o modelo2.

[]'s

Anônimo disse...

Amigo, esse seu post, por mais que tenha sido curtinho, me quebrou um galhão. Obrigada!