Wann ist inverse = false für OneToMany-Beziehungen zwischen NHibernate und Hibernate zu verwenden?

70

Ich habe versucht, das umgekehrte Attribut von Hibernate in den Griff zu bekommen, und es scheint nur eines dieser Dinge zu sein, die konzeptionell schwierig sind.

Das Wesentliche, das ich erhalte, ist, dass wenn Sie eine übergeordnete Entität (z. B. Parent) haben, die eine Sammlung von untergeordneten Objekten unter Verwendung einer Eins-zu-Viele- Zuordnung hat, die Einstellung inverse = true in der Zuordnung Hibernate mitteilt, dass die andere Seite (das untergeordnete Objekt) ) ist dafür verantwortlich, sich selbst zu aktualisieren, um die Fremdschlüsselreferenz in seiner Tabelle beizubehalten. “

Dies zu tun scheint zwei Vorteile zu haben, wenn Sie der Sammlung in Ihrem Code untergeordnete Elemente hinzufügen und dann das übergeordnete Element (mit Cascade-All-Set) speichern : Sie speichern einen unnötigen Treffer in der Datenbank (da Hibernate dies ohne inverse Set denkt hat zwei Stellen, um die FK-Beziehung zu aktualisieren) und gemäß den offiziellen Dokumenten:

Wenn die Spalte einer Zuordnung als NICHT NULL deklariert wird, kann NHibernate beim Erstellen oder Aktualisieren der Zuordnung Verstöße gegen Einschränkungen verursachen. Um dieses Problem zu vermeiden, müssen Sie eine bidirektionale Zuordnung mit dem vielwertigen Ende (dem Satz oder der Tasche) verwenden, das als invers = "wahr" markiert ist.

Dies alles scheint bisher sinnvoll zu sein. Was ich nicht verstehe, ist Folgendes: Wann möchten Sie inverse = true NICHT für eine Eins-zu-Viele-Beziehung verwenden?

James Allen
quelle

Antworten:

82

Wie Matthieu sagt, ist der einzige Fall, in dem Sie nicht inverse = true setzen möchten, der, in dem es keinen Sinn macht, dass das Kind für die Aktualisierung selbst verantwortlich ist, beispielsweise in dem Fall, in dem das Kind keine Kenntnis von seinem Elternteil hat.

Versuchen wir eine reale Welt und überhaupt kein erfundenes Beispiel:

<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>

Spionagemeister können Spione haben, aber Spione wissen nie, wer ihr Spionagemeister ist, weil wir die Viele-zu-Eins-Beziehung nicht in die Spionageklasse aufgenommen haben. Außerdem kann ein Spion (bequemerweise) zum Schurken werden und muss daher nicht mit einem Spionagemeister in Verbindung gebracht werden. Wir können Entitäten wie folgt erstellen:

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);

In einem solchen Fall würden Sie die FK-Spalte auf null setzen, da der Vorgang des Speicherns von sm in die SpyMaster-Tabelle und die Spy-Tabelle eingefügt wird und erst danach die Spy-Tabelle aktualisiert wird, um die FK festzulegen. In diesem Fall würde die FK niemals aktualisiert werden, wenn wir inverse = true setzen würden.

Nigel
quelle
Das hat bei mir nicht funktioniert. Das Update wird nie ausgeführt. Es fügt sie nur ein.
BradLaney
29

Trotz der mit hohen Stimmen akzeptierten Antwort habe ich eine andere Antwort darauf.

Betrachten Sie ein Klassendiagramm mit diesen Beziehungen:

Übergeordnet => Liste der Elemente
Item => Parent

Niemand hat jemals gesagt, dass die Relation Item => Parent redundant zur Relation Parent => Items ist. Ein Element kann auf jedes übergeordnete Element verweisen.

In Ihrer Anwendung wissen Sie jedoch, dass die Beziehungen redundant sind . Sie wissen, dass die Beziehungen nicht separat in der Datenbank gespeichert werden müssen. Sie entscheiden sich also, es in einem einzelnen Fremdschlüssel zu speichern , der vom Element zum übergeordneten Element zeigt. Diese minimalen Informationen reichen aus, um die Liste und die Referenz wieder aufzubauen .

Alles, was Sie tun müssen, um dies mit NH abzubilden, ist:

  • Verwenden Sie für beide Beziehungen denselben Fremdschlüssel
  • Teilen Sie NH mit, dass eine (die Liste) für die andere redundant ist und beim Speichern des Objekts ignoriert werden kann. (Damit macht NH eigentlich inverse="true")

Dies sind die Gedanken, die für die Umkehrung relevant sind. Nichts anderes. Es ist keine Wahl, es gibt nur einen Weg zur korrekten Zuordnung.


Das Spionageproblem : Es ist eine völlig andere Diskussion, wenn Sie eine Referenz vom Gegenstand zum Elternteil unterstützen möchten. Dies liegt an Ihrem Geschäftsmodell, NH trifft hier keine Entscheidungen. Wenn eine der Beziehungen fehlt, gibt es natürlich keine Redundanz und keine Verwendung von Inverse.

Missbrauch: Wenn Sie inverse = "true" für eine Liste verwenden, die keine Redundanz im Speicher hat, wird sie einfach nicht gespeichert. Wenn Sie inverse = "true" nicht angeben, falls vorhanden, speichert NH die redundanten Informationen möglicherweise zweimal.

Stefan Steinegger
quelle
Ich fand diese Antwort besser verständlich als die akzeptierte Antwort
r3try
Viel einfacher und auf den Punkt als meine Antwort .
Daniel Schilling
15

Wenn Sie eine unidirektionale Zuordnung wünschen, dh, dass die Kinder nicht zum übergeordneten Element navigieren können. In diesem Fall sollte Ihre FK-Spalte NULLABLE sein, da die untergeordneten Elemente vor dem übergeordneten Element gespeichert werden.

MatthieuGD
quelle