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

128

La JPAspécification (Java Persistence API) propose deux méthodes différentes pour spécifier les clés composites d'entité: @IdClasset @EmbeddedId.

J'utilise les deux annotations sur mes entités mappées, mais cela s'avère être un gros gâchis pour les personnes qui ne sont pas très familières avec JPA.

Je souhaite adopter une seule façon de spécifier les clés composites. Lequel est vraiment le meilleur? Pourquoi?

Sakana
la source

Réponses:

86

Je considère que @EmbeddedIdc'est probablement plus détaillé car avec @IdClassvous ne pouvez pas accéder à l'ensemble de l'objet clé primaire en utilisant un opérateur d'accès au champ. En utilisant le, @EmbeddedIdvous pouvez faire comme ceci:

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

Cela donne une notion 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 aux champs.

Une autre différence avec @IdClasset @EmbeddedIdest quand il s'agit d'écrire HQL:

Avec @IdClassvous écrivez:

sélectionnez e.name dans Employee e

et avec @EmbeddedIdvous devez écrire:

sélectionnez e.employeeId.name dans Employee e

Vous devez écrire plus de texte pour la même requête. Certains peuvent argumenter que cela diffère d'un langage plus naturel comme celui promu par IdClass. Mais la plupart du temps, comprendre dès la requête qu'un champ donné fait partie de la clé composite est d'une aide précieuse.

Jorge Ferreira
la source
10
Bien que je sois d'accord avec l'explication donnée ci-dessus, je voudrais également ajouter un cas d'utilisation unique pour @IdClassmême si je préfère @EmbeddedIddans la plupart des situations ( Je dois le savoir à partir d'une session d'Antonio Goncalves. Ce qu'il a suggéré, c'est que nous pourrions utiliser le @IdClassdans le cas où le composite la classe de clé n'est pas accessible ou provient d'un autre module ou d'un code hérité où nous ne pouvons pas ajouter d'annotation .Dans ces scénarios @IdClass, nous allons nous donner un moyen de notre.
Gaurav Rawat
1
Je pense qu'il est possible que les cas d'utilisation de l' @IdClassannotation donnée par @Gaurav soient la raison même pour laquelle la spécification JPA répertorie les deux méthodes de création d'une clé composite .. @IdClasset@EmbeddidId
kapad
20

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

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

L'utilisation de @Idavec une classe marquée comme @Embeddableétant l'approche la plus naturelle. La @Embeddablebalise peut quand même être utilisée pour les valeurs incorporables de clé non primaire. Il vous permet de traiter la clé primaire composée comme une propriété unique et permet la réutilisation de la @Embeddableclasse dans d'autres tables.

La deuxième approche la plus naturelle est l'utilisation de la @EmbeddedIdbalise. Ici, la classe de clé primaire ne peut pas être utilisée dans d'autres tables car ce n'est pas une @Embeddableentité, mais elle nous permet de traiter la clé comme un attribut unique d'une classe.

Enfin, l'utilisation des annotations @IdClasset @Idnous permet de mapper la classe de clé primaire composée en utilisant les 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'y a pas de mécanisme pour remplacer cela), et la classe de clé primaire doit respecter les mêmes obligations qu'avec les deux autres techniques. Le seul avantage de cette approche est sa capacité à «masquer» l'utilisation de la classe de clé primaire à l'interface de l'entité englobante. L' @IdClassannotation prend un paramètre de valeur de type Classe, qui doit être la classe à utiliser comme clé primaire composée. Les champs qui correspondent 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

Adelin
la source
17

J'ai découvert une instance où je devais utiliser EmbeddedId au lieu d'IdClass. Dans ce scénario, une table de jointure a 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 des lignes dans la table de jointure. Je ne pouvais pas le faire fonctionner de cette façon. Heureusement, "Java Persistence With Hibernate" a une section dédiée à ce sujet. Une solution proposée était très similaire à la mienne, mais elle utilisait EmbeddedId à la place. J'ai modelé mes objets après ceux du livre, ils se comportent maintenant correctement.

laz
la source
13

Pour autant que je sache, si votre PK composite contient du FK, il est plus facile et plus simple à utiliser @IdClass

Avec, @EmbeddedIdvous devez définir le mappage pour votre colonne FK deux fois, une fois dans @Embeddedableet une fois pour comme c'est-à-dire @ManyToOne@ManyToOnedoit être en lecture seule ( @PrimaryKeyJoinColumn) car vous ne pouvez pas avoir une colonne définie dans deux variables (conflits possibles).
Vous devez donc définir votre FK en utilisant une saisie simple @Embeddedable.

Sur l'autre site, l'utilisation de @IdClasscette situation peut être traitée beaucoup plus facilement, comme indiqué dans les clés primaires via les relations OneToOne et ManyToOne :

Exemple d'annotation d'identifiant ManyToOne 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;
    }
}
Ondrej Bozek
la source
1
N'oubliez pas d'ajouter des getters à votre classe PK
Sonata
@Sonata pourquoi avons-nous besoin des getters? Je l'ai essayé sans getters / setters et cela fonctionne très bien
xagaffar
Merci pour l'exemple de la classe id! Bien que j'aie fini par avoir besoin d'implémenter Serializable également. Vous pouvez également ajouter des getters et des setters, surtout si votre IDE peut les générer automatiquement.
Starwarswii
8

Je pense que le principal avantage est que nous pourrions utiliser @GeneratedValuepour l'identifiant lors de l'utilisation du @IdClass? Je suis sûr que nous ne pouvons pas utiliser @GeneratedValuepour @EmbeddedId.

Bertie
la source
1
N'est-il pas possible d'utiliser @GeneratedValue dans Embeddedid ??
Kayser
1
Je l'ai utilisé avec succès avec EmbeddedId mais apparemment, son support varie selon la base de données. Cela vaut également pour son utilisation avec IdClass. La spécification dit: "L'annotation GeneratedValue ne peut être utilisée de manière portative que pour des clés primaires simples (c'est-à-dire non composites)."
BPS
4

La clé composite ne doit pas avoir de @Idpropriété lorsqu'elle @EmbeddedIdest utilisée.

BK
la source
1

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

Aleks Ben Maza
la source