Dúvida aparentemente básica de JPA/Hibernate

Como eu faço para atualizar uma “Superclasse” para uma “Subclasse” no BD, usando jpa/hibernate?

Exemplo:

EntityManager em = criaEM();
Superclasse superC = new Superclasse();
//set outros campos da Superclasse...
superC = em.merge(superC);

Até aqui nada de novo. Agora eu quero que a Superclasse seja atualizada para uma classe especializada (Subclasse). Pelo pouco que sei sobre o merge, eu teria que executar algo como:

Subclasse subC = new Subclasse();
subC.setId(superC.getID);
//... "deep copy" dos campos da Superclasse p/ Subclasse...
subC = em.merge(subC);

Quando eu executo um código semelhante ao de cima, com a estratégia “Table per subclass” (@Inheritance(strategy = InheritanceType.JOINED)), o hibernate tenta persistir novamente um registro na tabela “Superclasse”, com o mesmo ID anterior, o que gera, obviamente, uma violação de chave primária.(java.sql.SQLIntegrityConstraintViolationException).

Existe alguma forma de fazer isso, sem ter que usar HQL ou SQL? Acredito que isso seja um problema bastante comum e que deve existir a resposta p/ isso aqui mesmo no fórum, mas até o momento eu não encontrei e nem mesmo sei que palavras-chave usar para fazer essa pesquisa…

Cara, eu não entendi muito bem o que você quer, mas vamos lá

Você tem uma SuperClasse e uma subclasse, por exemplo: Pessoa (superclass) e PessoaFisica (subclasse), certo? Você primeiro persistiu a SuperClasse (Pessoa) e agora quer persistir a subclasse (Pessoa) usando os dados da SuperClasse? Se for isso, acredito que sua lógica está errada.

Primeiro: em um relacionamento de herança usando JOINED, só deve haver uma chave primária, que fica na SUPERCLASSE. Eg:

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Pessoa {
  @Id
  @GeneratedValue(strategy=GeneretedValueType.IDENTITY)
  private Integer id;
  //getters and setters
}

E a subclasse

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

Agora ao invés de instanciar primeiro a classe Pessoa e depois a PessoaFisica, você instancia diretamente a classe PessoaFisica

PessoaFisica  pessoa = new PessoaFisica();
pessoa......
em.persist(pessoa)

Não sei se te ajuda, mas é assim que devemos trabalhar com herança no JPA.

OBS: não sei se o código acima está 100% correto, pq eu digitei ele diretamente aqui no editor

Obrigado pela resposta, mas não é tão simples assim.
Pegando uma carona no seu exemplo, seria como se eu tivesse uma PessoaFisica “Maria” cadastrada no banco. Depois de algum tempo, “Maria” passou a ser uma fornecedora (a classe Fornecedor é subclasse de PessoaFisica). Se eu soubesse que Maria já seria “Fornecedor” desde o início, td bem. Mas o projeto que estou participando possui uma hierarquia grande de objetos que poderão ser atualizados p/ um tipo especializado…

Entendeu o problema? :?

Agora eu entendi melhor :wink: , mas ainda não conheço uma solução adequada, pois como eu já disse acima, no caso de heranças, apenas um ID é usado (na Super Classe), portanto uma PessoaFisica teria um ID e o Fornecedor outro ID (mesmo sendo a mesma pessoa).

O que você pode fazer é trazer do banco os dados da PessoaFisica, criar um novo Fornecedor, e manualmente copiar os dados (ou pode usar o BeanUtils.copy para isso) e finalmente persistir o novo Fornecedor. O que irá acontecer é que será gerado um NOVO ID.

Outra solução (que afeta a arquitetura das bases) é deixar um CAMPO e conforme o conteúdo desse campo, você iria saber o tipo da pessoa.

Herança é um caso sério. Deve ser usada em casos bem específicos. Se puder, prefira composição ao invés de Herança.

Mesmo sem usar banco, o uso de herança, quando o papel do objeto muda com o tempo, é equivocado.

Melhor criar um objeto Pessoa, sem herdar nada, que tem como atributo uma referência a Tipo (interface). Quem a implementa são Fornecedor e Cliente. E aí, sempre que houver mudança de responsabilidade, mude o implementação de Tipo.

A respeito de usar o mesmo ID, não vejo problema, até pq é isso mesmo que eu quero. No problema em questão, estou utilizando a estratégia de uma tabela por subclasse, (InheritanceType.JOINED) compartilhando a chave primária da superclasse. Se eu persisto um “Fornecedor” com id = 1 e faço a seguinte consulta, por exemplo:

o hibernate já traz a instância da classe Fornecedor(mesmo utilizando a classe PessoaFisica na consulta), o que é excelente. Deve existir uma forma de “atualizar” um objeto da classe pai p/ um objeto da classe filho, sem precisar fazer um hql ou sql “na mão grande”…

[]'s

[quote=alancq]A respeito de usar o mesmo ID, não vejo problema, até pq é isso mesmo que eu quero.
[]'s[/quote]

Eu quis dizer o contrário, eles não vao usar o MESMO ID. A PessoaFisica, Fornecedor, Cliente, whatever, cada um terá o seu próprio ID.
Como na sua herança (usando JOINED) as entidades relacionadas possuem apenas um atributo ID (que está na super classe), portanto o ID não pode se repetir (cada entidade terá seu próprio ID).

Ok, vamos entrar nos detalhes… O projeto é um pouco grande. É uma aplicação integrada, com vários módulos. Envolve pessoas físicas, pessoas jurídicas, fornecedores, contadores, instituições e um tanto de outros papéis que são “pessoas”. Algumas dessas “pessoas” podem ser apenas pessoa física (exemplo: ResponsavelPessoaJuridica). Outras apenas pessoa jurídica (ex: Instituicao). Outras que podem ser pessoa física ou pessoa juridica (Ex.: Fornecedor) (or exclusivo - XOR). Uma pessoa pode assumir mais de um papel (ex.: um contador que é fornecedor e é responsável por tal empresa (ResponsavelPessoaJuridica)).

Estamos procurando o melhor modelo para esses requisitos. Talvez tenhamos que mudar td realmente, mas não era esse o foco principal do post. Daqui a pouco o post será movido p/ fórum Java Básico (Orientação a Objetos)… Será que não tem jeito de fazer um merge(subclasse) atualizando uma superclasse previamente persistida?

Obrigado por responderem!

+1 post. Este último com embasamento teórico melhor e um hack que pode ajudar outras pessoas que venham a passar por este mesmo problema.
http://forum.hibernate.org/viewtopic.php?t=961485&highlight=inheritancetype+share

Alguem encontrou uma solução ?

Estou procurando a solução também.
O “hack” mencionado é algo que me veio a mente, mas ainda não existe solução para isto?
A quantas andam a evolução deste Hibernate??
Faz qualquer “novato” ter medo de “mergulhar” no framework. Vou continuar procurando.
E, por favor, não vamos tentar arrumar desculpas para fugir da modelagem e representação corretas em OO. Diferente do que o amigo de Campinas/SP disse no post do link acima:

  • ‘uma pessoa deve poder sim se tornar um devedor’
  • Maria pode se tornar fornecedora
  • Maria pode ser contratada e se tornar funcionária
  • Maria pode ser demitida, abrir uma empresa e se tornar contato de um cliente (PJ)!!!