Ich habe eine Situation, in der ich getrennte Objekte erneut an eine Sitzung im Ruhezustand anhängen muss, obwohl möglicherweise bereits ein Objekt mit derselben Identität in der Sitzung vorhanden ist, was zu Fehlern führen kann.
Im Moment kann ich eines von zwei Dingen tun.
getHibernateTemplate().update( obj )
Dies funktioniert genau dann, wenn in der Ruhezustandssitzung noch kein Objekt vorhanden ist. Es werden Ausnahmen ausgelöst, die besagen, dass ein Objekt mit der angegebenen Kennung bereits in der Sitzung vorhanden ist, wenn ich es später benötige.getHibernateTemplate().merge( obj )
Dies funktioniert genau dann, wenn in der Ruhezustandssitzung ein Objekt vorhanden ist. Ausnahmen werden ausgelöst, wenn das Objekt später in einer Sitzung sein soll, wenn ich dies verwende.
Wie kann ich in diesen beiden Szenarien generisch Sitzungen an Objekte anhängen? Ich möchte keine Ausnahmen verwenden, um den Ablauf der Lösung dieses Problems zu steuern, da es eine elegantere Lösung geben muss ...
refresh()
getrennte Entitäten nicht zulässt . Wenn ich mir die 2.0-Spezifikation ansehe, sehe ich keine Rechtfertigung. nur dass es nicht erlaubt ist.*Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.
Mehr finden Sie in Abschnitt 9.3.2lock(LockMode.NONE)
tatsächlich für ein vorübergehendes Objekt aufgerufen werden, und es verbindet die Entität erneut mit der Sitzung. Siehe stackoverflow.com/a/3683370/14379Alle diese Antworten verfehlen eine wichtige Unterscheidung. update () wird verwendet, um Ihr Objektdiagramm an eine Sitzung (erneut) anzuhängen. Die Objekte, die Sie übergeben, werden verwaltet.
merge () ist eigentlich keine (Neu-) Anhangs-API. Beachten Sie, dass merge () einen Rückgabewert hat? Dies liegt daran, dass Sie das verwaltete Diagramm zurückgeben, das möglicherweise nicht das Diagramm ist, das Sie übergeben haben. merge () ist eine JPA-API und ihr Verhalten wird von der JPA-Spezifikation bestimmt. Wenn das Objekt, das Sie an merge () übergeben, bereits verwaltet ist (bereits mit der Sitzung verknüpft), ist dies das Diagramm, mit dem der Ruhezustand arbeitet. Das übergebene Objekt ist dasselbe Objekt, das von merge () zurückgegeben wurde. Wenn jedoch das Objekt, das Sie an merge () übergeben, getrennt wird, erstellt Hibernate ein neues Objektdiagramm, das verwaltet wird, und kopiert den Status aus Ihrem getrennten Diagramm in das neue verwaltete Diagramm. Auch dies wird von der JPA-Spezifikation diktiert und geregelt.
In Bezug auf eine generische Strategie für "Sicherstellen, dass diese Entität verwaltet wird oder verwaltet wird" hängt dies davon ab, ob Sie auch noch nicht eingefügte Daten berücksichtigen möchten. Angenommen, Sie verwenden so etwas wie
Beachten Sie, dass ich saveOrUpdate () anstelle von update () verwendet habe. Wenn Sie nicht möchten, dass noch nicht eingefügte Daten hier verarbeitet werden, verwenden Sie stattdessen update () ...
quelle
Session.contains(Object)
prüft durch Bezugnahme. Wenn bereits eine andere Entität dieselbe Zeile in der Sitzung darstellt und Sie eine getrennte Instanz übergeben, wird eine Ausnahme angezeigt.Session.contains(Object)
Referenz überprüft wird, dass eine andere Entität dieselbe Zeile in der Sitzung darstellt, wird false zurückgegeben und aktualisiert.Undiplomatische Antwort: Sie suchen wahrscheinlich nach einem erweiterten Persistenzkontext. Dies ist einer der Hauptgründe für das Seam Framework ... Wenn Sie Schwierigkeiten haben, Hibernate im Frühjahr zu verwenden, lesen Sie diese Dokumentation von Seam.
Diplomatische Antwort: Dies ist in den Hibernate-Dokumenten beschrieben . Wenn Sie weitere Informationen benötigen, lesen Sie Abschnitt 9.3.2 der Java-Persistenz mit Ruhezustand mit dem Titel "Arbeiten mit getrennten Objekten". Ich würde Ihnen dringend empfehlen, dieses Buch zu erwerben, wenn Sie mit Hibernate mehr als CRUD machen.
quelle
Wenn Sie sicher sind, dass Ihre Entität nicht geändert wurde (oder wenn Sie damit einverstanden sind, dass Änderungen verloren gehen), können Sie sie mit Sperre erneut an die Sitzung anhängen.
Es wird nichts gesperrt, aber die Entität wird aus dem Sitzungscache abgerufen oder (falls nicht dort gefunden) aus der Datenbank gelesen.
Es ist sehr nützlich, LazyInitException zu verhindern, wenn Sie Beziehungen von einer "alten" (beispielsweise von der HttpSession) Entität aus navigieren. Sie "hängen" zuerst die Entität wieder an.
Die Verwendung von get funktioniert möglicherweise auch, es sei denn, Sie erhalten eine Zuordnung der Vererbung (wodurch bereits eine Ausnahme für getId () ausgelöst wird).
quelle
Session.lock(entity, LockMode.NONE)
scheitert mit Ausnahme der Aussage: Die nicht initialisierte vorübergehende Sammlung konnte nicht wieder zugeordnet werden. Wie kann das überwunden werden?Session.find()
API-Methode. Vielleicht meinst duSession.load(Object object, Serializable id)
.Entitätszustände
JPA definiert die folgenden Entitätszustände:
Neu (vorübergehend)
Ein neu erstelltes Objekt, das noch nie einem Ruhezustand
Session
(akaPersistence Context
) zugeordnet wurde und keiner Datenbanktabellenzeile zugeordnet ist, befindet sich im Status Neu (vorübergehend).Um persistent zu werden, müssen wir entweder die
EntityManager#persist
Methode explizit aufrufen oder den transitiven Persistenzmechanismus verwenden.Dauerhaft (verwaltet)
Eine persistente Entität wurde einer Datenbanktabellenzeile zugeordnet und wird vom aktuell ausgeführten Persistenzkontext verwaltet. Jede an einer solchen Entität vorgenommene Änderung wird erkannt und an die Datenbank weitergegeben (während der Sitzungsspülzeit).
Mit Hibernate müssen wir keine INSERT / UPDATE / DELETE-Anweisungen mehr ausführen. Hibernate verwendet einen Transaktions-Write-Behind- Arbeitsstil und Änderungen werden im allerletzten verantwortlichen Moment während des Stroms synchronisiert
Session
Spülzeit .Freistehend
Sobald der aktuell ausgeführte Persistenzkontext geschlossen ist, werden alle zuvor verwalteten Entitäten getrennt. Aufeinanderfolgende Änderungen werden nicht mehr verfolgt und es findet keine automatische Datenbanksynchronisierung statt.
Übergänge des Entitätsstatus
Sie können den Entitätsstatus mit verschiedenen Methoden ändern, die durch das definiert sind
EntityManager
Schnittstelle .Betrachten Sie das folgende Diagramm, um die Übergänge des JPA-Entitätsstatus besser zu verstehen:
Wenn Sie JPA verwenden, um eine getrennte Entität einer aktiven zuzuordnen
EntityManager
, können Sie die Zusammenführung verwenden verwenden.Wenn Sie die native Hibernate-API verwenden,
merge
können Sie außerdem eine getrennte Entität mithilfe der Aktualisierungsmethoden erneut einer aktiven Hibernate-Sitzung zuordnen, wie im folgenden Diagramm dargestellt:Zusammenführen einer getrennten Einheit
Durch die Zusammenführung wird der Status der getrennten Entität (Quelle) in eine Instanz einer verwalteten Entität (Ziel) kopiert.
Angenommen, wir haben die folgende
Book
Entität beibehalten, und jetzt wird die Entität getrennt, da dieEntityManager
zum Bestehen der Entität verwendete Entität geschlossen wurde:Während sich die Entität im getrennten Zustand befindet, ändern wir sie wie folgt:
Jetzt möchten wir die Änderungen an die Datenbank weitergeben, damit wir die
merge
Methode aufrufen können :Und Hibernate wird die folgenden SQL-Anweisungen ausführen:
Wenn die fusionierende Entität im aktuellen keine Entsprechung hat
EntityManager
, wird ein neuer Entitätsschnappschuss aus der Datenbank abgerufen.Sobald eine verwaltete Entität vorhanden ist, kopiert JPA den Status der getrennten Entität auf die aktuell verwaltete Entität. Während des Persistenzkontexts
flush
wird ein UPDATE generiert, wenn der Dirty-Checking-Mechanismus feststellt, dass sich die verwaltete Entität geändert hat.Erneutes Anhängen einer getrennten Entität
Ruhezustand, aber nicht JPA unterstützt das erneute Anhängen über die
update
Methode.Ein Ruhezustand
Session
kann nur ein Entitätsobjekt für eine bestimmte Datenbankzeile zuordnen. Dies liegt daran, dass der Persistenzkontext als speicherinterner Cache (Cache der ersten Ebene) fungiert und einem bestimmten Schlüssel (Entitätstyp und Datenbankkennung) nur ein Wert (Entität) zugeordnet ist.Eine Entität kann nur dann erneut zugeordnet werden, wenn dem aktuellen Ruhezustand noch kein anderes JVM-Objekt (das mit derselben Datenbankzeile übereinstimmt) bereits zugeordnet ist
Session
.In Anbetracht dessen
Book
, dass wir dieBook
Entität beibehalten haben und dass wir sie geändert haben, als sich die Entität im getrennten Zustand befand:Wir können die abgetrennte Entität wie folgt wieder anbringen:
Und Hibernate führt die folgende SQL-Anweisung aus:
Im Gegensatz dazu
merge
wird die bereitgestellte getrennte Entität dem aktuellen Persistenzkontext neu zugeordnet, und während des Flushs wird ein UPDATE geplant, unabhängig davon, ob die Entität geändert wurde oder nicht.Um dies zu verhindern, können Sie die
@SelectBeforeUpdate
Annotation Hibernate verwenden, die eine SELECT-Anweisung auslöst, die den geladenen Status abruft, der dann vom Dirty-Checking-Mechanismus verwendet wird.Achten Sie auf die NonUniqueObjectException
Ein Problem, das auftreten kann,
update
besteht darin, dass der Persistenzkontext bereits eine Entitätsreferenz mit derselben ID und demselben Typ wie im folgenden Beispiel enthält:Wenn Sie den obigen Testfall ausführen, wird Hibernate a auslösen,
NonUniqueObjectException
da der zweiteEntityManager
bereits eineBook
Entität mit derselben Kennung enthält wie die, an die wir übergebenupdate
, und der Persistenzkontext nicht zwei Darstellungen derselben Entität enthalten kann.Fazit
Die
merge
Methode ist vorzuziehen, wenn Sie optimistische Sperren verwenden, um verlorene Aktualisierungen zu vermeiden. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .Dies
update
ist gut für Stapelaktualisierungen, da es die zusätzliche SELECT-Anweisung verhindern kann, die durch diemerge
Operation generiert wird , wodurch die Ausführungszeit für Stapelaktualisierungen verkürzt wird.quelle
@SelectBeforeUpdate
Anmerkung gewundert. Wann wird die Auswahl ausgelöst? Beim Aufrufenupdate
, kurz vor dem Löschen oder spielt es keine Rolle (es könnte wichtig sein, wenn der Ruhezustand alle kommentierten Entitäten in einem Aufruf vor dem Löschen abruft)?@SelectBeforeUpdate
löst SELECT während der Persistenzkontextoperation ausflush
. Weitere Informationen finden Sie in dergetDatabaseSnapshot
Methode inDefaultFlushEntityEventListener
.Ich ging zurück zum JavaDoc
org.hibernate.Session
und fand Folgendes:So
update()
,saveOrUpdate()
,lock()
,replicate()
undmerge()
sind die Kandidaten - Optionen.update()
: Löst eine Ausnahme aus, wenn eine persistente Instanz mit derselben Kennung vorhanden ist.saveOrUpdate()
: Entweder speichern oder aktualisierenlock()
: Veraltetreplicate()
: Behalten Sie den Status der angegebenen getrennten Instanz bei und verwenden Sie den aktuellen Bezeichnerwert erneut.merge()
: Gibt ein persistentes Objekt mit demselben Bezeichner zurück. Die angegebene Instanz wird der Sitzung nicht zugeordnet.Daher
lock()
sollte nicht sofort verwendet werden und basierend auf den funktionalen Anforderungen können einer oder mehrere von ihnen ausgewählt werden.quelle
Ich habe es in C # mit NHibernate so gemacht, aber in Java sollte es genauso funktionieren:
First Lock wurde für jedes Objekt aufgerufen, da Contains immer falsch war. Das Problem ist, dass NHibernate Objekte nach Datenbank-ID und Typ vergleicht. Contains verwendet die
equals
Methode, die anhand einer Referenz verglichen wird, wenn sie nicht überschrieben wird. Mit dieserequals
Methode funktioniert es ohne Ausnahmen:quelle
Session.contains(Object obj)
Überprüft die Referenz und erkennt keine andere Instanz, die dieselbe Zeile darstellt und bereits an sie angehängt ist.Hier meine generische Lösung für Entitäten mit einer Bezeichner-Eigenschaft.
Dies ist einer der wenigen Aspekte von .Net EntityFramework, die mir gefallen, die verschiedenen Anhängeoptionen für geänderte Entitäten und deren Eigenschaften.
quelle
Ich habe eine Lösung gefunden, um ein Objekt aus dem Persistenzspeicher zu "aktualisieren", das andere Objekte berücksichtigt, die möglicherweise bereits an die Sitzung angehängt sind:
quelle
Leider können anscheinend noch keine Kommentare hinzugefügt werden.
Verwenden von Hibernate 3.5.0-Final
Während die
Session#lock
Methode dies veraltete, schlägt der Javadoc vor , zu verwendenSession#buildLockRequest(LockOptions)#lock(entity)
und wenn Sie sicherstellen, dass Ihre Assoziationen habencascade=lock
, ist das verzögerte Laden auch kein Problem.Meine Attach-Methode sieht also ein bisschen so aus
Erste Tests legen nahe, dass es ein Genuss ist.
quelle
Vielleicht verhält es sich bei Eclipselink etwas anders. Um getrennte Objekte erneut anzuhängen, ohne veraltete Daten zu erhalten, gehe ich normalerweise wie folgt vor:
und optional einen zweiten Schritt (um Caches ungültig zu machen):
quelle
Versuchen Sie getHibernateTemplate (). replicate (Entität, ReplicationMode.LATEST_VERSION)
quelle
In der ursprünglichen Post, gibt es zwei Methoden,
update(obj)
undmerge(obj)
dass an der Arbeit erwähnt, jedoch in entgegengesetzten Umständen. Wenn dies wirklich zutrifft, testen Sie zuerst, ob sich das Objekt bereits in der Sitzung befindet, und rufenupdate(obj)
Sie dann auf, wenn dies der Fall ist. Andernfalls rufen Sie aufmerge(obj)
.Der Test für die Existenz in der Sitzung ist
session.contains(obj)
. Daher würde ich denken, dass der folgende Pseudocode funktionieren würde:quelle
Um dieses Objekt erneut anzuhängen, müssen Sie merge () verwenden.
Diese Methode akzeptiert im Parameter, dass Ihre Entität getrennt wurde, und gibt eine Entität zurück, die angehängt und aus der Datenbank neu geladen wird.
quelle
Das Aufrufen von zuerst merge () (um die persistente Instanz zu aktualisieren) und dann lock (LockMode.NONE) (um die aktuelle Instanz anzuhängen, nicht die von merge () zurückgegebene) scheint für einige Anwendungsfälle zu funktionieren.
quelle
Eigentum
hibernate.allow_refresh_detached_entity
hat den Trick für mich getan. Da dies jedoch eine allgemeine Regel ist, ist es nicht sehr geeignet, wenn Sie dies nur in einigen Fällen tun möchten. Ich hoffe, es hilft.Getestet im Ruhezustand 5.4.9
SessionFactoryOptionsBuilder
quelle
Die Unterstützung für den Ruhezustand hängt die getrennte Entität auf verschiedene Weise wieder an, siehe Benutzerhandbuch für den Ruhezustand .
quelle
quelle