Mise en veille prolongée un à une association d'entité PK partagée entre 3 classes

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

  •  18-09-2019
  •  | 
  •  

Question

Je veux un unidirectionnel one-to-one relation entre les objets de 3 classes java: Personne à coeur, et personne au foie. Je veux que les objets de partager la même PK dire chaque personne a un cœur et le foie correspondant, où person.person_id = heart.heart_id = liver.liver_id. Je ne veux pas fusionner les 3 tables en 1 parce que chacun a des charges de champs. Voici mon code (la plupart du temps en fonction de la réponse acceptée pour cette question ):

@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
}

Configuration I la session et procédez comme suit:

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

Alors si je lien entre les objets personne avec ses organes, et de l'enregistrer, je reçois une erreur (NOTE: Je suis le même comportement quand je viens d'utiliser 2 classes par exemple personne et coeur):

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

org.hibernate.id.IdentifierGenerationException: tentative d'attribuer id null un à une propriété: personne

Cependant, il fonctionne si je mets la relation dans les deux sens:

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

Mais sûrement cela ne devrait pas être nécessaire pour les relations unidirectionnels?
Vive

ps. Curieusement, je remarque cela fonctionne (sauve les deux objets à la DB) quand je viens d'utiliser 2 classes par exemple Personne et le cœur, et je viens de mettre en lien dans l'autre sens:

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

Je ne sais pas pourquoi cela fonctionne (il me semble logique de cette personne est l'objet parent, car il génère automatiquement son propre PK, et les autres utilise que, de sorte que ce que vous devriez tous avoir à installer), mais de toute façon Je ne peux pas comprendre comment appliquer cette méthode de travail à ma situation 3 classe ...

Était-ce utile?

La solution

Je déteste te le dire, mais vous avez là une relation bidirectionnelle. La personne a une référence au cœur et le foie et chacun de ceux-ci ont une référence à la personne. Les annotations que vous avez mis en place sur les propriétés d'identification de votre coeur et le foie disent spécifiquement qu'ils obtiennent la valeur de leur propriété Id en déléguant à leur propriété de personne. Dans les exemples que vous avez montré que ne définissez pas, vous avez pas la propriété de personne sur ces gars-là encore et ainsi, ils peuvent évidemment pas obtenir leur identifiant.

Vous pouvez définir cette relation comme un véritable OneToOne unidirectionnel, qui est documenté dans la documentation des annotations Hibernate:

@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 vous pouvez changer notre entité des objets légèrement pour rationaliser accrochage des deux côtés de la relation telle que:

@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
}

Autres conseils

  

Je n'ai pas essayé, mais je dirais que ....

Une différence entre les deux côtés est que la classe Person n'a pas mappedBy dans sa cartographie.

Alors, pour chaque One-To-One, Person a la valeur de référence, celui qui Hibernate considèrent comme officiel. Au contraire, sur les autres objets, le mappedBy indique à Hibernate de ne pas utiliser la valeur, mais aller à cet objet et considérer la propriété mappée sur le cet objet.


Pour vérifier si je ne me trompe pas, vous pouvez définir uniquement les valeurs qui n'ont pas mappedBy et enregistrer l'objet Person (car il est celui qui a les cascades), et voir si le résultat est correct ..; - )

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top