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…
Agora eu entendi melhor , 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”…
[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?
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)!!!