Hibernate one-to-one associação entidade com PK compartilhada entre 3 classes

StackOverflow https://stackoverflow.com/questions/1867235

  •  18-09-2019
  •  | 
  •  

Pergunta

Eu quero um unidirecional um-para-um entre objetos de 3 classes Java: Pessoa de coração, e de pessoa para fígado. Eu quero os objetos para compartilhar a mesma PK ou seja, cada pessoa tem um coração correspondente e fígado, onde person.person_id = heart.heart_id = liver.liver_id. Eu não quero fundir as 3 mesas em 1 porque cada um tem um monte de campos. Aqui está o meu código (principalmente com base na resposta aceita para esta questão ):

@Entity
public class Person {
   public long personId;
   private String name;
   public Heart heart;
   public Liver liver;
   // other fields

   @Id
   @GeneratedValue
   public long getPersonId() {return personId;}

   @OneToOne(cascade = CascadeType.ALL)
   @PrimaryKeyJoinColumn
   public Heart getHeart() {return heart;}

   @OneToOne(cascade = CascadeType.ALL)
   @PrimaryKeyJoinColumn
   public Liver getLiver() {return liver;}

   // other getters and setters and constructors
}


@Entity
public class Heart {
   private long heartId;
   private int bpm;
   private Person person;
   // other fields

   @Id
   @GenericGenerator(
      name = "generator",
      strategy = "foreign",
      parameters = @Parameter(name = "property", value = "person")
   )
   @GeneratedValue(generator = "generator")
   public long getHeartId() {return heardId;}

   @OneToOne(mappedBy="heart")
   @PrimaryKeyJoinColumn
   public Person getPerson() {return person;}

   // other getters and setters and constructors
}


@Entity
public class Liver {
   private long liverId;
   private boolean healthy;
   private Person person;
   // other fields

   // the rest uses the same hibernate annotation as Heart
}

Configuração I a sessão e fazer o seguinte:

Person jack = new Person();
jack.setName("jack");
Heart heart = new Heart();
heart.setBpm(80);
Liver liver = new Liver();
liver.setHealthy(true);

Então, se eu ligar o objeto pessoa com ele é órgãos, e guardá-lo, eu recebo um erro (NOTA: Eu tenho o mesmo comportamento quando eu usei apenas 2 classes por exemplo Pessoa e coração):

jack.setHeart(heart);
jack.setLiver(liver);
session.save(jack);

org.hibernate.id.IdentifierGenerationException: tentou id atribuir a partir nula one-to-one propriedade: pessoa

No entanto ele funciona se eu definir a relação de duas formas:

jack.setHeart(heart);
heart.setPerson(jack);
jack.setLiver(liver);
liver.setPerson(jack);
session.save(jack);

Mas, certamente, isso não deve ser necessário para relacionamentos unidirecionais?
Felicidades

ps. Curiosamente, eu noto que funciona (poupa ambos os objetos para o DB) quando eu uso apenas 2 classes por exemplo Pessoa e coração, e eu apenas definir o link para o outro lado:

heart.setPerson(jack);
session.save(heart);

Eu não tenho idéia por que isso funciona (parece lógico para mim essa pessoa é o objeto pai, como ele gera automaticamente o seu próprio PK, e os outros usam isso, então isso é tudo que você deve ter a configuração), mas de qualquer maneira Eu não consigo descobrir como aplicar este método de trabalho para a minha situação 3-class ...

Foi útil?

Solução

Eu odeio dizer isso, mas você tem uma relação bidirecional lá. A pessoa tem uma referência para o coração eo fígado e cada um deles tem um back referência à pessoa. As anotações que você configurou nas propriedades Id do seu coração e fígado estão dizendo especificamente que eles obtenham o valor de sua propriedade Id, delegando em sua propriedade Pessoa. Nos exemplos que você mostrou que não funcionam, você não definir a propriedade Pessoa sobre esses caras ainda e por isso, eles obviamente não pode obter o seu valor Id.

Você pode definir essa relação se como um verdadeiro OneToOne unidirecional, que está documentado na documentação do Hibernate anotações:

@Entity
public class Body {
    @Id
    public Long getId() { return id; }

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    public Heart getHeart() {
        return heart;
    }
    ...
}


@Entity
public class Heart {
    @Id
    public Long getId() { ...}
}

ou você pode mudar a nossa entidade objetos ligeiramente para agilizar ligar ambos os lados da relação, tais como:

@Entity
public class Person {
   public long personId;
   private String name;
   public Heart heart;
   public Liver liver;
   // other fields

   @Id
   @GeneratedValue
   public long getPersonId() {return personId;}

   @OneToOne(cascade = CascadeType.ALL)
   @PrimaryKeyJoinColumn
   public Heart getHeart() {return heart;}

   public void setHeart(Heart heart){
      this.heart = heart;
      this.heart.setPerson(this);
   }

   @OneToOne(cascade = CascadeType.ALL)
   @PrimaryKeyJoinColumn
   public Liver getLiver() {return liver;}

   public void setLiver(Liver liver){
      this.liver = liver;
      this.liver.setPerson(this);
   }
   // other getters and setters and constructors
}

Outras dicas

Eu não tentei isso, mas eu diria que ....

A diferença entre os dois lados é que a classe Person não tem mappedBy em seu mapeamento.

Assim, para cada um-para-um, Person tem o valor de referência, o que Hibernate considerar como oficial. Pelo contrário, em outros objetos, o mappedBy indica ao Hibernate para não usar o valor, mas ir para esse objeto e considerar a propriedade mapeado no objeto.


Para verificar se estou certo, você pode definir apenas os valores que não têm mappedBy, e salvar o objeto Person (porque é a única que tem as cascatas), e ver se o resultado está correcto ..; - )

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top