web-dev-qa-db-fra.com

JPA @OneToOne avec Shared ID - Puis-je faire mieux?

Je travaille avec un schéma existant que je préfère ne pas modifier. Le schéma a une relation un à un entre les tables Person et VitalStats, où Person a une clé primaire et VitalStats utilise le même champ que sa clé primaire et sa clé étrangère vers Person, ce qui signifie que sa valeur est la valeur du PK correspondant de personne.

Ces enregistrements sont créés par des processus externes et mon code JPA n'a jamais besoin de mettre à jour VitalStats. Pour mon modèle d'objet, j'aimerais que ma classe Person contienne un membre VitalStats, MAIS:

Quand j'essaye

@Entity
public class Person{
    private long id;
    @Id
    public long getId(){ return id; }

    private VitalStats vs;
    @OneToOne(mappedBy = “person”)
    public VitalStats getVs() { return vs; }
}

@Entity
    public class VitalStats{
     private Person person;
    @OneToOne
    public Person getPerson() { return person; }
}

J'ai le problème que VitalStats n'a pas de @Id, ce qui ne fonctionne pas pour une @Entity. \

Si j'essaye

@Id @OneToOne
public Person getPerson() { return person; }

qui résout le problème @Id mais nécessite que la personne soit sérialisable. Nous y reviendrons.

Je pourrais rendre VitalStats @Embeddable et le connecter à Person via un @ElementCollection, mais alors il faudrait y accéder en tant que collection, même si je sais qu'il n'y a qu'un seul élément. Faisable, mais à la fois un peu ennuyeux et un peu déroutant.

Alors, qu'est-ce qui m'empêche de simplement dire que Person implémente Serializable? Rien, vraiment, sauf que j'aime que tout dans mon code soit là pour une raison, et je ne vois aucune logique à cela, ce qui rend mon code moins lisible.

En attendant, je viens de remplacer le champ Personne dans VitalStats par un long personId et de créer ce VitalStats @Id, donc maintenant le @OneToOne fonctionne.

Toutes ces solutions à ce qui me semble (simple) être un problème simple sont un peu maladroites, donc je me demande si je manque quelque chose, ou si quelqu'un peut au moins m'expliquer pourquoi la personne doit être sérialisable.

TIA

60
Michael

Pour mapper une association un à un à l'aide de clés primaires partagées, utilisez @PrimaryKeyJoinColumn et @MapsId annotation.

Sections pertinentes de la documentation de référence Hibernate:

PrimaryKeyJoinColumn

L'annotation PrimaryKeyJoinColumn indique que la clé primaire de l'entité est utilisée comme valeur de clé étrangère pour l'entité associée.

MapsId

L'annotation MapsId demande à Hibernate de copier l'identifiant d'une autre entité associée. Dans le jargon Hibernate, il est connu comme un générateur étranger mais le mappage JPA se lit mieux et est encouragé

Person.Java

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private VitalStats vitalStats;       
}

VitalStats.Java

@Entity
public class VitalStats 
{
    @Id @Column(name="vitalstats_id") Long id;

    @MapsId 
    @OneToOne(mappedBy = "vitalStats")
    @JoinColumn(name = "vitalstats_id")   //same name as id @Column
    private Person person;

    private String stats;
}

Table de base de données des personnes

CREATE TABLE  person (
  person_id   bigint(20) NOT NULL auto_increment,
  name        varchar(255) default NULL,
  PRIMARY KEY  (`person_id`)
) 

Table de base de données VitalStats

CREATE TABLE  vitalstats 
(
  vitalstats_id  bigint(20) NOT NULL,
  stats          varchar(255) default NULL,
  PRIMARY KEY  (`vitalstats_id`)
)
87
Joel Hudon

Dans mon cas, cela a fait l'affaire:

Classe parent:

public class User implements Serializable {
  private static final long serialVersionUID = 1L;

  /** auto generated id (primary key) */
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(unique = true, nullable = false)
  private Long id;

  /** user settings */
  @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
  private Setting setting;
}

Classe enfant:

public class Setting implements Serializable {
  private static final long serialVersionUID = 1L;

  /** setting id = user id */
  @Id
  @Column(unique = true, nullable = false)
  private Long id;

  /** user with this associated settings */
  @MapsId
  @OneToOne
  @JoinColumn(name = "id")
  private User user;
}
17
camposer