web-dev-qa-db-fra.com

Quand utiliser inverse = false sur les relations NHibernate / Hibernate OneToMany?

J'ai essayé de maîtriser l'attribut inverse d'Hibernate, et il semble que ce ne soit qu'une de ces choses qui est conceptuellement difficile.

L'essentiel que je reçois est que lorsque vous avez une entité parent (par exemple Parent) qui a une collection d'objets enfants en utilisant un mappage un-à-plusieurs , définir inverse = true sur le mappage indique à Hibernate que "l'autre côté (l'enfant) a la responsabilité de se mettre à jour pour maintenir la référence de clé étrangère dans sa table".

Cela semble avoir 2 avantages lorsqu'il s'agit d'ajouter des enfants à la collection dans votre code, puis d'enregistrer le parent (avec cascade-all set): vous enregistrez un hit inutile sur la base de données (car sans ensemble inverse, Hibernate pense qu'il a deux endroits pour mettre à jour la relation FK), et selon les documents officiels:

Si la colonne d'une association est déclarée NON NULL, NHibernate peut provoquer des violations de contraintes lors de la création ou de la mise à jour de l'association. Pour éviter ce problème, vous devez utiliser une association bidirectionnelle avec l'extrémité à plusieurs valeurs (l'ensemble ou le sac) marquée comme inverse = "true".

Tout cela semble logique jusqu'à présent. Ce que je ne comprends pas, c'est: quand voudriez-vous [~ # ~] pas [~ # ~] utiliser invers = true sur un seul à plusieurs?

70
James Allen

Comme le dit Matthieu, le seul cas où vous ne voudriez pas définir inverse = true est celui où il n'est pas logique que l'enfant soit responsable de se mettre à jour, comme dans le cas où l'enfant n'a aucune connaissance de ses parents.

Essayons un monde réel, et pas du tout un exemple artificiel:

<class name="SpyMaster" table="SpyMaster" lazy="true">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
  <set name="Spies" table="Spy" cascade="save-update">
    <key column="SpyMasterId"/>
    <one-to-many class="Spy"/>
  </set>
</class>

<class name="Spy" table="Spy" lazy="true">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
</class>

Les maîtres-espions peuvent avoir des espions, mais les espions ne savent jamais qui est leur maître-espion, car nous n'avons pas inclus la relation plusieurs-à-un dans la classe d'espionnage. Aussi (commodément) un espion peut devenir voyou et n'a donc pas besoin d'être associé à un maître-espion. Nous pouvons créer des entités comme suit:

var sm = new SpyMaster
{
    Name = "Head of Operation Treadstone"
};
sm.Spies.Add(new Spy
{
    Name = "Bourne",
    //SpyMaster = sm // Can't do this
});
session.Save(sm);

Dans un tel cas, vous définiriez la colonne FK pour être nullable car l'acte d'enregistrement de sm s'insérerait dans la table SpyMaster et la table Spy, et ce n'est qu'après cela qu'il mettrait à jour la table Spy pour définir le FK. Dans ce cas, si nous devions définir inverse = true, le FK ne serait jamais mis à jour.

81
Nigel

Malgré la réponse acceptée au vote élevé, j'ai une autre réponse à cela.

Considérons un diagramme de classes avec ces relations:

 Parent => liste d'articles 
 Article => Parent 

Personne n'a jamais dit que la relation Item => Parent est redondante à la relation Parent => Items. Un élément peut référencer n'importe quel parent.

Mais dans votre application, vous savez que les relations sont redondantes. Vous savez que les relations n'ont pas besoin d'être stockées séparément dans la base de données. Vous décidez donc de le stocker dans une clé étrangère unique, pointant de l'élément vers le parent. Ces informations minimales sont suffisantes pour construire la liste et la référence en arrière.

Tout ce que vous devez faire pour mapper cela avec NH est:

  • utiliser la même clé étrangère pour les deux relations
  • dire à NH que l'un (la liste) est redondant pour l'autre et pourrait être ignoré lors du stockage de l'objet. (C'est ce que NH fait réellement avec inverse="true")

Ce sont les pensées qui sont pertinentes pour l'inverse. Rien d'autre. Ce n'est pas un choix, il n'y a qu'une seule façon de mapper correctement.


Le problème d'espionnage: C'est une discussion complètement différente si vous voulez supporter une référence de l'élément au parent. Cela dépend de votre modèle commercial, NH ne prend aucune décision à ce sujet. Si l'une des relations est manquante, il n'y a bien sûr pas de redondance et pas d'utilisation d'inverse.

Mauvaise utilisation: Si vous utilisez inverse = "true" sur une liste qui n'a pas de redondance en mémoire, elle n'est tout simplement pas stockée. Si vous ne spécifiez pas l'inverse = "true" s'il doit y être, NH peut stocker les informations redondantes deux fois.

29
Stefan Steinegger

Si vous souhaitez avoir une association unidirectionnelle, c'est-à-dire que les enfants ne peuvent pas naviguer vers le parent. Si tel est le cas, votre colonne FK doit être NULLABLE car les enfants seront enregistrés avant le parent.

15
MatthieuGD