Quelle est la différence entre les associations JPA et Hibernate unidirectionnelles et bidirectionnelles?

135

Quelle est la différence entre les associations unidirectionnelles et bidirectionnelles?

Étant donné que les tables générées dans la base de données sont toutes identiques, la seule différence que j'ai trouvée est que chaque côté des associations bidirectionnelles aura un renvoi à l'autre, et non unidirectionnel.

Ceci est une association unidirectionnelle

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

L'association bidirectionnelle

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

La différence est de savoir si le groupe contient une référence de l'utilisateur.

Alors je me demande si c'est la seule différence? qui est recommandé?

hguser
la source
7
Le groupe saura désormais quels utilisateurs il contient. Je ne pense pas que ce soit une petite différence.
Satadru Biswas
5
Les relations bidirectionnelles sont devenues un chaos pour moi en ce qui concerne la mise à jour. :)
diyoda_

Réponses:

152

La principale différence est que la relation bidirectionnelle fournit un accès de navigation dans les deux sens, de sorte que vous pouvez accéder à l'autre côté sans requêtes explicites. En outre, il vous permet d'appliquer des options en cascade dans les deux sens.

Notez que l'accès à la navigation n'est pas toujours bon, en particulier pour les relations «un-à-très-plusieurs» et «plusieurs-à-très-plusieurs». Imaginez un Groupcontenant des milliers de Users:

  • Comment y accéderiez-vous? Avec autant de Users, vous devez généralement appliquer un filtrage et / ou une pagination, de sorte que vous devez quand même exécuter une requête (à moins que vous n'utilisiez le filtrage de collection , ce qui ressemble à un hack pour moi). Certains développeurs ont tendance à appliquer un filtrage en mémoire dans de tels cas, ce qui n'est évidemment pas bon pour les performances. Notez qu'une telle relation peut encourager ce type de développeurs à l'utiliser sans tenir compte des implications en termes de performances.

  • Comment ajouteriez-vous de nouveaux Users au Group? Heureusement, Hibernate examine le côté propriétaire de la relation lors de sa persistance, vous ne pouvez donc que définir User.group. Toutefois, si vous souhaitez que les objets en mémoire restent cohérents, vous devez également ajouter Userà Group.users. Mais cela obligerait Hibernate à récupérer tous les éléments de Group.usersla base de données!

Donc, je ne peux pas être d'accord avec la recommandation des meilleures pratiques . Vous devez concevoir des relations bidirectionnelles avec soin, en tenant compte des cas d'utilisation (avez-vous besoin d'un accès de navigation dans les deux sens?) Et des implications possibles sur les performances.

Voir également:

axtavt
la source
Salut, merci, semble que vous êtes un expert de la mise en veille prolongée, puisque vous avez répondu à ma question: stackoverflow.com/questions/5350770/… . Et maintenant, je ne suis pas sûr de la relation qui tient, puisque je ne peux pas écrire plus dans le commentaire, donc je le poste ici dpaste.de/J85m .Veuillez vérifier si possible. :)
hguser
@hguser: Si vous décidez enfin de rendre votre relation bidirectionnelle, je pense qu'il serait préférable d'appeler setGroup()depuis addUser()pour que les deux côtés restent cohérents.
axtavt
que diriez-vous si je n'appelle pas le setGroup () dans le group.addUser ()?
hguser
@hguser: La relation ne serait pas persistante, voir le point 2 de la réponse. Vous pouvez appeler setGroup()sans addUser(), mais cela entraînerait un état incohérent des objets en mémoire.
axtavt
J'ai trouvé que je ne peux pas faire une cartographie correcte, pouvez-vous prendre du temps pour vérifier mon projet sur github? c'est un petit projet.
hguser
31

Il existe deux différences principales.

Accéder aux côtés de l'association

Le premier est lié à la manière dont vous accéderez à la relation. Pour une association unidirectionnelle, vous pouvez naviguer dans l'association à partir d'une seule extrémité.

Ainsi, pour une @ManyToOneassociation unidirectionnelle , cela signifie que vous ne pouvez accéder à la relation que du côté enfant où réside la clé étrangère.

Si vous avez une @OneToManyassociation unidirectionnelle , cela signifie que vous ne pouvez accéder à la relation que du côté parent où réside la clé étrangère.

Pour l' @OneToManyassociation bidirectionnelle , vous pouvez naviguer dans l'association des deux manières, soit du côté parent, soit du côté enfant.

Vous devez également utiliser des méthodes utilitaires d'ajout / suppression pour les associations bidirectionnelles afin de vous assurer que les deux côtés sont correctement synchronisés .

Performance

Le deuxième aspect est lié à la performance.

  1. Pour @OneToMany, les associations unidirectionnelles ne fonctionnent pas aussi bien que les bidirectionnels .
  2. Pour @OneToOne, une association bidirectionnelle provoquera une récupération rapide du parent si Hibernate ne peut pas dire si le proxy doit être attribué ou une valeur nulle .
  3. Car @ManyToMany, le type de collection fait toute la différence car il est Setsplus performant queLists .
Vlad Mihalcea
la source
11

En termes de codage, une relation bidirectionnelle est plus complexe à mettre en œuvre car l'application est responsable de maintenir les deux côtés synchronisés conformément à la spécification JPA 5 (à la page 42). Malheureusement, l'exemple donné dans la spécification ne donne pas plus de détails, donc il ne donne pas une idée du niveau de complexité.

Lorsque vous n'utilisez pas de cache de deuxième niveau, il n'est généralement pas un problème de ne pas implémenter correctement les méthodes de relation car les instances sont supprimées à la fin de la transaction.

Lors de l'utilisation du cache de deuxième niveau, si quelque chose est corrompu en raison de méthodes de gestion des relations mal implémentées, cela signifie que les autres transactions verront également les éléments corrompus (le cache de deuxième niveau est global).

Une relation bidirectionnelle correctement implémentée peut simplifier les requêtes et le code, mais ne doit pas être utilisée si elle n'a pas vraiment de sens en termes de logique métier.

Constantino Cronemberger
la source
9

Je ne suis pas sûr à 100% que ce soit la seule différence, mais c'est la principale différence. Il est également recommandé d'avoir des associations bidirectionnelles par la documentation Hibernate:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

Plus précisément:

Préférez les associations bidirectionnelles: les associations unidirectionnelles sont plus difficiles à interroger. Dans une application volumineuse, presque toutes les associations doivent être navigables dans les deux sens dans les requêtes.

J'ai personnellement un léger problème avec cette recommandation générale - il me semble qu'il y a des cas où un enfant n'a aucune raison pratique de connaître son parent (par exemple, pourquoi un article de commande doit-il connaître la commande dont il s'agit? associé à?), mais j'y vois de la valeur dans une partie raisonnable du temps également. Et comme la bidirectionnalité ne fait vraiment aucun mal, je ne trouve pas cela trop répréhensible pour y adhérer.

kvista
la source
La bidirectionnalité peut faire mal dans certains cas, comme l'a expliqué axtavt! Je ferais très attention à chaque relation mappée dans une entité Hibernate! Vous pouvez vous retrouver avec un temps infini nécessaire pour charger des choses dans Hibernate, à moins que vous ne sachiez exactement ce que vous faites. Réfléchissez soigneusement aux cas d'utilisation et utilisez différentes classes de modèles pour différents cas d'utilisation. Par ex. dans une liste de choses, je n'ai pas besoin de tous les objets associés, juste une étiquette et l'ID. L'entité de liste est donc différente (et très simple) de l'entité de détail, pour moi.
cslotty le