web-dev-qa-db-fra.com

Quelle annotation devrais-je utiliser: @IdClass ou @EmbeddedId

La spécification JPA (Java Persistence API) dispose de 2 manières différentes de spécifier des clés composites d'entité: @IdClass et @EmbeddedId

J'utilise les deux annotations sur mes entités mappées, mais cela s'avère être un gros désastre pour les personnes qui ne connaissent pas très bien JPA

Je souhaite adopter un seul moyen de spécifier des clés composites. Lequel est vraiment le meilleur? Pourquoi?

110
sakana

Je considère que @EmbeddedId est probablement plus détaillé car avec @IdClass, vous ne pouvez pas accéder à tout l’objet de clé primaire à l’aide d’un opérateur d’accès au champ. En utilisant le @EmbeddedId vous pouvez faire comme ceci:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

Cela donne une idée claire des champs qui composent la clé composite, car ils sont tous agrégés dans une classe accessible via un opérateur d'accès au champ.

Une autre différence avec @IdClass et @EmbeddedId concerne l’écriture en HQL:

Avec @IdClass vous écrivez:

@EmbeddedId vous devez écrire:

IdClass. Mais la plupart du temps, il est extrêmement utile de comprendre dès la requête qu'un champ donné fait partie de la clé composite.

77
Jorge Ferreira

J'ai découvert une instance où je devais utiliser EmbeddedId au lieu d'IdClass. Dans ce scénario, une table de jointure contient des colonnes supplémentaires définies. J'ai tenté de résoudre ce problème en utilisant IdClass pour représenter la clé d'une entité qui représente explicitement les lignes de la table de jointure. Je ne pouvais pas le faire fonctionner de cette façon. Heureusement, "Java Persistence With Hibernate" contient une section consacrée à ce sujet. Une solution proposée était très similaire à la mienne, mais elle utilisait plutôt EmbeddedId. J'ai modelé mes objets d'après ceux du livre, il se comporte maintenant correctement.

17
laz

Il existe trois stratégies pour utiliser une clé primaire composée: 

  • Marquez-le en tant que @Embeddable et ajoutez à votre classe d'entités une propriété normale, identifiée par @Id.
  • Ajoutez à votre classe d’entités une propriété normale, identifiée par @EmbeddedId.
  • Ajoutez des propriétés à votre classe d'entité pour tous ses champs, marquez-les avec @Id et marquez votre classe d'entité avec @IdClass, en fournissant la classe de votre classe de clé primaire.

L'utilisation de @Id avec une classe marquée @Embeddable est l'approche la plus naturelle. La balise @Embeddable peut de toute façon être utilisée pour les valeurs intégrables par clé non primaire. Il vous permet de traiter la clé primaire composée comme une propriété unique et de réutiliser la classe @Embeddable dans d'autres tables. 

La prochaine approche la plus naturelle consiste à utiliser la balise @EmbeddedId. Ici, la classe de clé primaire ne peut pas être utilisée dans d'autres tables car ce n'est pas une entité @Embeddable, mais elle nous permet de traiter la clé comme un attribut unique D'une classe. 

Enfin, l’utilisation des annotations @IdClass et @Id nous permet de mapper la classe de clé primaire composée à l’aide des propriétés de l’entité elle-même correspondant aux noms des propriétés de la classe de clé primaire. Les noms doivent correspondre (il n’existe aucun mécanisme pour y remédier) et la classe de clé primaire doit respecter les mêmes obligations que pour les deux autres techniques. Le seul avantage de cette approche réside dans sa capacité à "masquer" l'utilisation de la classe de clé primaire de l'interface de l'entité englobante. L'annotation @IdClass prend un paramètre de valeur de type classe, qui doit être la classe à utiliser comme clé primaire composée. Les champs correspondant aux propriétés de la classe de clé primaire à utiliser doivent tous être annotés avec @Id.

Référence: http://www.apress.com/us/book/9781430228509

13
Adelin

Autant que je sache, si votre PK composite contient du FK, il est plus simple et plus rapide d'utiliser @IdClass

Avec @EmbeddedId, vous devez définir le mappage de votre colonne FK deux fois, une seule fois dans @Embeddedable et une fois comme suit: @ManyToOne@ManyToOne doit être en lecture seule (@PrimaryKeyJoinColumn) car vous ne pouvez pas définir une colonne dans deux variables (conflits possibles).
Vous devez donc définir votre FK en utilisant un type simple dans @Embeddedable.

Sur l’autre site utilisant @IdClass, cette situation peut être gérée beaucoup plus facilement, comme indiqué dans Clés primaires via les relations OneToOne et ManyToOne :

Exemple d'annotation ManyToOne id JPA 2.0

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

Exemple de classe d'identifiant JPA 2.0

...
public class PhonePK {
    private String type;
    private long owner;

    public PhonePK() {}

    public PhonePK(String type, long owner) {
        this.type = type;
        this.owner = owner;
    }

    public boolean equals(Object object) {
        if (object instanceof PhonePK) {
            PhonePK pk = (PhonePK)object;
            return type.equals(pk.type) && owner == pk.owner;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return type.hashCode() + owner;
    }
}
11
Ondrej Bozek

Je pense que le principal avantage est que nous pourrions utiliser @GeneratedValue pour l'identifiant en utilisant le @IdClass? Je suis sûr que nous ne pouvons pas utiliser @GeneratedValue pour @EmbeddedId.

6
bertie

La clé composite ne doit pas avoir de propriété @Id lorsque @EmbeddedId est utilisé. 

4
B.K

Avec EmbeddedId, vous pouvez utiliser la clause IN dans HQL, par exemple: FROM Entity WHERE id IN :ids où id est un EmbeddedId alors qu'il est difficile d'obtenir le même résultat avec IdClass, vous voudrez faire quelque chose comme FROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

0
Aleks Ben Maza