Question

La spécification JPA (API de persistance Java) 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 énorme gâchis 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?

Était-ce utile?

La solution

Je considère que @EmbeddedId est probablement plus détaillé car avec @IdClass , vous ne pouvez pas accéder à l'objet de clé primaire complet à l'aide d'un opérateur d'accès de champ. En utilisant @EmbeddedId , procédez comme suit:

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

Ceci 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 entre @IdClass et @EmbeddedId réside dans l'écriture de HQL:

Avec @IdClass , vous écrivez:

select e.name from Employee e

et avec @EmbeddedId , vous devez écrire:

select e.employeeId.name from Employee e

Vous devez écrire plus de texte pour la même requête. Certains pourraient soutenir que cela diffère d'un langage plus naturel comme celui promu par 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.

Autres conseils

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 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 plutôt EmbeddedId. J'ai modelé mes objets d'après ceux du livre. Il se comporte maintenant correctement.

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

  • Marquez-le comme @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 primaire classe de clés.

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 deuxième 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 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 dans la classe de clé primaire. Les noms doivent correspondre (il n’existe pas de 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 value de type Class, qui doit être la classe à utiliser en tant que 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

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

.

Avec @EmbeddedId , vous devez définir le mappage de votre colonne FK à deux reprises, une seule fois dans @Embeddedable et une fois en tant que @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'identificateurs 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;
    }
}

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

La clé composite

ne doit pas avoir une propriété @Id lorsque @EmbeddedId est utilisé.

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

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