Problema com herança de entidades

3 respostas
L

Tenho um modelo de dados em que algumas entidades heram atributos comuns de uma superclasse. Estou usando InheritanceType.JOINED, em que o hibernate/jpa cria uma tabela para os atributos da superclasse (Fornecedor) e tabelas contendo somente os atributos adicionados pelas subclasses (FornecedorHotel e FornecedorDiversos). Até aí, tudo bem.

O problema surge quando eu tento inserir um FornecedorHotel (fornece serviço de hospedagem) que é também um FornecedorDiversos (fornece aluguel de salas, serviço de buffet, etc). O fornecedorHotel é inserido sem problemas, sendo os dados basicos inseridos na tabela Fornecedor (superclasse) e os dados especificos inseridos na tabela FornecedorHotel (subclasse). No momento de inserir os dados especificos de FornecedorDiversos (outra subclasse) ocorre o erro, pois o hibernate tenta inserir novamente na tabela Fornecedor ocorrendo violação de PK.

Existe alguma forma de instruir o hibernate/jpa a inserir somente os dados especificos na tabela FornecedorDiversos? Ele deveria entender que a superclasse já possui o ID em questão e inserir somente os dados especificos na subclasse, mas ele não faz isso. Alguém já passou por algo parecido?

Seguem os pseudo-códigos para análise:

Entity
@Inheritance(strategy=InheritanceType.JOINED) 
public class Fornecedor implements Serializable {
        private Long id;
	private String nome;
	private String email;
	private Long cnpj;
	private Boolean ativo;

// getters e setters
}

@Entity
@Table(name="FORNECEDOR_HOTEL")
@Inheritance(strategy=InheritanceType.JOINED)
@PrimaryKeyJoinColumn(name="ID_FORNECEDOR") 
public class FornecedorHotel extends Fornecedor {	
	private Date dataCadastro;
        private String tipoApartamento;
        private Integer tipoQuarto; // 1 - single, 2 - double, 3 - triple, 4 - quadruple
        private Integer tipoCama; // 1 - solteiro, 2 - casal
	private Set<TipoProduto> produtos = new HashSet<TipoProduto>(0);

//getters e setters
}

@Entity
@Table(name="FORNECEDOR_DIVERSOS")
@Inheritance(strategy=InheritanceType.JOINED)
@PrimaryKeyJoinColumn(name="ID_FORNECEDOR") 
public class FornecedorDiversos extends Fornecedor {	
	private Date dataCadastro;
	private Set<TipoProduto> produtos = new HashSet<TipoProduto>(0);

//getters e setters
}


public class App {
      public static void main(String[] args) {
           ...

               Map<String, Set<TipoProduto>> mapaTiposProdutos = prepararProdutos(produtosEJB);
			
		Set<TipoProduto> tiposProdutoHotel = mapaProdutos.get("HOT");
		Set<TipoProduto> tiposProdutoDiversos = mapaProdutos.get("DIV");

      	       FornecedorHotel hot = new FornecedorHotel();
		hot.setId(new Long(1));
		hot.setNome("FORNECEDOR_001");
		hot.setEmail("[email removido]");
		hot.setCnpj(123456789l);
		hot.setDataCadastro(data.getTime());
		hot.setAtivo(Boolean.TRUE);
                hot.setTipoApartamento("LUXO");
		hot.setProdutos(tiposProdutoHotel );
			
		fornecedorEJB.cadastrarFornecedorHotel(hot);  //ok
			
		FornecedorDiversos diversos = new FornecedorDiversos();
		diversos.setId(new Long(1));
		diversos.setDataCadastro(data.getTime());
		diversos.setProdutos(tiposProdutoDiversos );

                fornecedorEJB.cadastrarFornecedorDiversos(diversos); //erro - tanto chamando persist quanto merge...

     }
}

3 Respostas

Hebert_Coelho

Parece que você está utilizando de forma errada.

Pq vc ta declarando JOINED em toda herança?

Eu sei que desse modo aqui funciona: JPA Uma Classe por Sub-Classe.

L

Olá jakefrog,

Fiz as implementações do post que vc indicou, mas também não consegui o resultado esperado. Ocorre o mesmo problema que eu estou tendo.

Para simular, basta tentar inserir um carro que seja um Beetle e uma Ferrari ao mesmo tempo. O desejado, é que se insira uma linha em Carro, uma linha em Beetle e uma linha em Ferrari assim:

Carro : id: 1 - nome: Herbie
Beetle: id: 1 - gasCapacity: 45
Ferrari: id: 1 - model: F200

Ao inserir o registro em Beetle, tudo ok, funciona, ele insere o registro em Carro e em Beetle. Ao tentar inserir o registro em Ferrari, da erro de constraint, pois ele tenta inserir em Carro novamente.

A solução que encontrei foi escrevendo uma NativeQuery, porém gostaria de saber se existe uma solução mais sofisticada.

drsmachado

O exemplo do carro está fora de cogitação, nenhum carro é um beetle e uma ferrari ao mesmo tempo.
Você poderia ter utilizado um exemplo mais simples. Um Homem é pai de dois Filhos. Cada filho possui um Filho, logo, cada Filho é um pai. Pais e Filhos são Pessoas.

Neste caso, sim, consegue-se exemplificar o que você precisa:

Pessoa:
id: 1
nome: “Joao PaiFilho”

Pai:
id: 1
nome: "Joao PaiFilho"
qtFilhos: 1

Filho:
id: 1
nome: "Joao PaiFilho"
irmaos: 1

Entendeu?

Agora, o problema está na modelagem. Fornecedor sempre será fornecedor, não importa o Servico ou Produto que ele forneça.
Opa, acho que apareceu alguma coisa nova aí.
Fornecedor pode fornecer (sério, não diga!) Servico e/ou Produto. Por que e/ou? Por que ele pode fornecer um buffet (produto) e os garçons, cozinheiros, copeiras que irão trabalhar no evento (serviços). Ou seja, eu criaria um enum contendo os elementos SERVICO, PRODUTO, PRODUTO_SERVICO e colocaria este enum como um atributo de Fornecedor.
Mas aí temos um outro problema, sabemos que o objeto fornecedor a ser persistido dispõe de produtos ou serviços (ou só produtos ou só serviços ou ambos). Como saber o que ele fornece?
Se você não precisa definir dados específicos de produtos e serviços, é fácil, basta criar uma classe, chamada TipoFornecido com os atributos id, nome e tipo (serviço ou produto) e uma coleção de TipoServico como atributo de Fornecedor e está ótimo. Agora, se precisa definir coisas como preço, estoque e outros detalhes já muda de figura.
Eu criaria uma interface, como TipoFornecido, que seria implementada por Produto e por Servico. Com isso, podemos usar polimorfismo e, então, instanciar um ou outro objeto quando necessário (mesmo que seja preciso fazer cast explícito para um dos tipos fornecidos, produto ou serviço).

Tudo isto foi para dizer que eu discordo do teu modelo. Você está olhando apenas por um lado, deveria ver de outras formas.
Se quiser aceitar a sugestão e alterar o que já fez (se puder, é claro) poderemos ajudar melhor.

Criado 7 de junho de 2012
Ultima resposta 8 de jun. de 2012
Respostas 3
Participantes 3