62 votes

JPA Hibernate Relation One-to-One

Je cherche à obtenir une relation un-à-un mais hibernatetool se plaint lors de la génération du schéma. Voici un exemple qui montre le problème:

@Entity
public class Person {
    @Id
    public int id;

    @OneToOne
    public OtherInfo otherInfo;

    reste des attributs ...
}

Person possède une relation un-à-un avec OtherInfo:

@Entity
public class OtherInfo {
    @Id
    @OneToOne(mappedBy="otherInfo")
    public Person person;

    reste des attributs ...
}

Person est le côté propriétaire de OtherInfo. OtherInfo est le côté possédé, donc person utilise mappedBy pour spécifier le nom de l'attribut "otherInfo" dans Person.

Je reçois l'erreur suivante lors de l'utilisation de hibernatetool pour générer le schéma de la base de données:

org.hibernate.MappingException: Could not determine type for: Person, at table: OtherInfo, for columns: [org.hibernate.mapping.Column(person)]
        at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:292)
        at org.hibernate.mapping.SimpleValue.createIdentifierGenerator(SimpleValue.java:175)
        at org.hibernate.cfg.Configuration.iterateGenerators(Configuration.java:743)
        at org.hibernate.cfg.Configuration.generateDropSchemaScript(Configuration.java:854)
        at org.hibernate.tool.hbm2ddl.SchemaExport.(SchemaExport.java:128)
        ...

Une idée de la raison? Est-ce que j'ai commis une erreur ou s'agit-il d'un bug Hibernate?

85voto

topchef Points 7473

JPA n'autorise pas l'annotation @Id sur un mappage OneToOne ou ManyToOne. Ce que vous essayez de faire est une association d'entités un à un avec une clé primaire partagée. Le cas le plus simple est une association unidirectionnelle un à un avec une clé partagée :

@Entity
public class Person {
    @Id
    private int id;

    @OneToOne
    @PrimaryKeyJoinColumn
    private OtherInfo otherInfo;

    reste des attributs ...
}

Le principal problème avec cela est que JPA ne fournit aucun support pour la génération de clé primaire partagée dans l'entité OtherInfo. Le célèbre livre Java Persistence with Hibernate by Bauer and King donne la solution suivante au problème en utilisant une extension Hibernate :

@Entity
public class OtherInfo {
    @Id @GeneratedValue(generator = "customForeignGenerator")
    @org.hibernate.annotations.GenericGenerator(
        name = "customForeignGenerator",
        strategy = "foreign",
        parameters = @Parameter(name = "property", value = "person")
    )
    private Long id;

    @OneToOne(mappedBy="otherInfo")
    @PrimaryKeyJoinColumn
    public Person person;

    reste des attributs ...
}

Voir aussi ici.

23voto

Mariusz Points 191

Cela devrait également fonctionner en utilisant l'annotation JPA 2.0 @MapsId au lieu du GenericGenerator d'Hibernate:

@Entity
public class Person {

    @Id
    @GeneratedValue
    public int id;

    @OneToOne
    @PrimaryKeyJoinColumn
    public OtherInfo otherInfo;

    reste des attributs ...
}

@Entity
public class OtherInfo {

    @Id
    public int id;

    @MapsId
    @OneToOne
    @JoinColumn(name="id")
    public Person person;

    reste des attributs ...
}

Plus de détails à ce sujet dans la documentation d'Hibernate 4.1 sous la section 5.1.2.2.7.

10voto

Igor Sadovnikov Points 101

Il vous suffit d'ajouter @JoinColumn(name="column_name") à la relation de l'entité Hôte. column_name est le nom de la colonne de la base de données dans la table person.

@Entity
public class Person {
    @Id
    public int id;

    @OneToOne
    @JoinColumn(name="other_info")
    public OtherInfo otherInfo;

    rest of attributes ...
}

Personne a une relation un-à-un avec OtherInfo: mappedBy="var_name" var_name est le nom de la variable pour otherInfo dans la classe Personne.

@Entity
public class OtherInfo {
    @Id
    @OneToOne(mappedBy="otherInfo")
    public Person person;

    rest of attributes ...
}

4voto

waxwing Points 10190

Je pense que vous avez toujours besoin de la propriété de clé primaire dans la classe OtherInfo.

@Entity
public class OtherInfo {
    @Id
    public int id;

    @OneToOne(mappedBy="otherInfo")
    public Person person;

    reste des attributs ...
}

De plus, vous devrez peut-être ajouter l'annotation @PrimaryKeyJoinColumn de l'autre côté de la correspondance. Je sais que Hibernate l'utilise par défaut. Mais je n'ai pas utilisé les annotations JPA, qui semblent vous obliger à spécifier comment fonctionne l'association.

2voto

Vodo-Siosk Baas Points 112

J'ai une meilleure façon de faire cela :

@Entity
public class Person {

    @OneToOne(cascade={javax.persistence.CascadeType.ALL})
    @JoinColumn(name = "`Id_OtherInfo`")
    public OtherInfo getOtherInfo() {
      return otherInfo;
    }

}

C'est tout

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X