Ich bin auf eine Situation gestoßen (die ich für seltsam halte, die aber möglicherweise ganz normal ist), in der ich EntityManager.getReference (LObj.getClass (), LObj.getId ()) verwende, um eine Datenbankentität abzurufen und das zurückgegebene Objekt an zu übergeben in einer anderen Tabelle bestehen bleiben.
Im Grunde war der Fluss also so:
Klasse TFacade { createT (FObj, AObj) { T TObj = neues T (); TObj.setF (FObj); TObj.setA (AObj); ... EntityManager.persist (TObj); ... L LObj = A.getL (); FObj.setL (LObj); FFacade.editF (FObj); }} }} @ TransactionAttributeType.REQUIRES_NEW Klasse FFacade { editF (FObj) { L LObj = FObj.getL (); LObj = EntityManager.getReference (LObj.getClass (), LObj.getId ()); ... EntityManager.merge (FObj); ... FLHFacade.create (FObj, LObj); }} }} @ TransactionAttributeType.REQUIRED Klasse FLHFacade { createFLH (FObj, LObj) { FLH FLHObj = neues FLH (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... }} }}
Ich habe die folgende Ausnahme "java.lang.IllegalArgumentException: Unbekannte Entität: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0" erhalten.
Nachdem ich mich eine Weile damit befasst hatte, stellte ich schließlich fest, dass ich aufgrund der Verwendung der EntityManager.getReference () -Methode die obige Ausnahme erhielt, da die Methode einen Proxy zurückgab.
Ich frage mich daher, wann es ratsam ist, die EntityManager.getReference () -Methode anstelle der EntityManager.find () -Methode zu verwenden .
EntityManager.getReference () löst eine EntityNotFoundException aus, wenn die gesuchte Entität nicht gefunden werden kann, was an sich sehr praktisch ist. Die EntityManager.find () -Methode gibt lediglich null zurück, wenn die Entität nicht gefunden werden kann.
In Bezug auf Transaktionsgrenzen klingt es für mich so, als müssten Sie die find () -Methode verwenden, bevor Sie die neu gefundene Entität an eine neue Transaktion übergeben. Wenn Sie die Methode getReference () verwenden, werden Sie wahrscheinlich mit der obigen Ausnahme in einer ähnlichen Situation wie ich enden.
quelle
Antworten:
Normalerweise verwende ich die getReference- Methode, wenn ich nicht auf den Datenbankstatus zugreifen muss (ich meine die Getter-Methode). Nur um den Zustand zu ändern (ich meine Setter-Methode). Wie Sie wissen sollten, gibt getReference ein Proxy-Objekt zurück, das eine leistungsstarke Funktion namens automatische Dirty Checking verwendet. Angenommen, das Folgende
Wenn ich die Methode find aufrufe, ruft der JPA-Anbieter hinter den Kulissen auf
Wenn ich die Methode getReference aufrufe , ruft der JPA-Anbieter hinter den Kulissen auf
Und du weißt warum ???
Wenn Sie getReference aufrufen, erhalten Sie ein Proxy-Objekt. So etwas wie dieses (JPA-Anbieter kümmert sich um die Implementierung dieses Proxys)
Vor dem Festschreiben der Transaktion wird dem JPA-Anbieter das Flag stateChanged angezeigt, um die Entität OR NOT person zu aktualisieren. Wenn nach der Update-Anweisung keine Zeilen aktualisiert werden, löst der JPA-Anbieter EntityNotFoundException gemäß der JPA-Spezifikation aus.
Grüße,
quelle
SELECT
Vorher ausUPDATE
, egal welches vonfind()
/getReference()
ich benutze. Was noch schlimmer ist,SELECT
durchläuft NON-LAZY-Beziehungen (Ausgabe neuerSELECTS
), obwohl ich nur ein einzelnes Feld in einer Entität aktualisieren möchte.Wie in diesem Artikel erläutert , wird davon ausgegangen, dass Sie eine übergeordnete
Post
Entität und ein untergeordnetes Element haben,PostComment
wie in der folgenden Abbildung dargestellt:Wenn Sie anrufen,
find
wenn Sie versuchen, die@ManyToOne
post
Zuordnung festzulegen:Hibernate führt die folgenden Anweisungen aus:
Die SELECT-Abfrage ist diesmal nutzlos, da die Post-Entität nicht abgerufen werden muss. Wir möchten nur die zugrunde liegende Spalte post_id Foreign Key festlegen.
Wenn Sie
getReference
stattdessen Folgendes verwenden :Dieses Mal gibt Hibernate nur die INSERT-Anweisung aus:
Im Gegensatz dazu
find
gibt dergetReference
einzige einen Entitätsproxy zurück, für den nur der Bezeichner festgelegt ist. Wenn Sie auf den Proxy zugreifen, wird die zugehörige SQL-Anweisung ausgelöst, solange der EntityManager noch geöffnet ist.In diesem Fall müssen wir jedoch nicht auf den Entitätsproxy zugreifen. Wir möchten den Fremdschlüssel nur an den zugrunde liegenden Tabellendatensatz weitergeben, sodass das Laden eines Proxys für diesen Anwendungsfall ausreicht.
Beim Laden eines Proxys müssen Sie beachten, dass eine LazyInitializationException ausgelöst werden kann, wenn Sie versuchen, auf die Proxy-Referenz zuzugreifen, nachdem der EntityManager geschlossen wurde. Weitere Informationen zum Umgang
LazyInitializationException
mit finden Sie in diesem Artikel .quelle
hibernate.jpa.compliance.proxy
Konfigurationseigenschaft , sodass Sie entweder die JPA-Konformität oder eine bessere Datenzugriffsleistung auswählen können.getReference
wird überhaupt benötigt, wenn es gerade ausreicht, eine neue Modellinstanz mit gesetztem PK zu setzen. Was vermisse ich?Da eine Referenz "verwaltet", aber nicht hydratisiert ist, können Sie eine Entität auch nach ID entfernen, ohne sie zuerst in den Speicher laden zu müssen.
Da Sie eine nicht verwaltete Entität nicht entfernen können, ist es einfach albern, alle Felder mit find (...) oder createQuery (...) zu laden, um sie dann sofort zu löschen.
quelle
EntityManager.getReference()
ist wirklich eine fehleranfällige Methode und es gibt wirklich sehr wenige Fälle, in denen ein Client-Code sie verwenden muss.Persönlich musste ich es nie benutzen.
EntityManager.getReference () und EntityManager.find (): Kein Unterschied in Bezug auf den Overhead
Ich bin mit der akzeptierten Antwort nicht einverstanden und insbesondere:
Es ist nicht das Verhalten, das ich mit Hibernate 5 bekomme, und der Javadoc von
getReference()
sagt so etwas nicht:EntityManager.getReference()
spart eine Abfrage, um die Entität in zwei Fällen abzurufen:1) Wenn die Entität im Persistenzkontext gespeichert ist, ist dies der Cache der ersten Ebene.
Und dieses Verhalten ist nicht spezifisch für
EntityManager.getReference()
,EntityManager.find()
erspart auch eine Abfrage zum Abrufen der Entität, wenn die Entität im Persistenzkontext gespeichert ist.Sie können den ersten Punkt mit einem beliebigen Beispiel überprüfen.
Sie können sich auch auf die eigentliche Hibernate-Implementierung verlassen.
Verlässt
EntityManager.getReference()
sich in der Tat auf diecreateProxyIfNecessary()
Methode derorg.hibernate.event.internal.DefaultLoadEventListener
Klasse, um die Entität zu laden.Hier ist seine Implementierung:
Der interessante Teil ist:
2) Wenn wir die Entität nicht effektiv manipulieren, wird das träge Abrufen des Javadoc wiederholt .
Um das effektive Laden der Entität sicherzustellen, muss eine Methode aufgerufen werden.
Der Gewinn würde also mit einem Szenario zusammenhängen, in dem wir eine Entität laden möchten, ohne sie verwenden zu müssen? Im Rahmen von Anwendungen ist dieser Bedarf wirklich ungewöhnlich und außerdem ist das
getReference()
Verhalten sehr irreführend, wenn Sie den nächsten Teil lesen.Warum sollten Sie EntityManager.find () gegenüber EntityManager.getReference () bevorzugen?
In Bezug auf den Overhead
getReference()
ist nicht besser alsfind()
wie im vorherigen Punkt diskutiert.Warum also den einen oder anderen benutzen?
Das Aufrufen
getReference()
kann eine träge abgerufene Entität zurückgeben.Hier bezieht sich das verzögerte Abrufen nicht auf Beziehungen der Entität, sondern auf die Entität selbst. Dies
bedeutet, dass wenn wir aufrufen
getReference()
und dann der Persistenzkontext geschlossen wird, die Entität möglicherweise nie geladen wird und das Ergebnis daher wirklich unvorhersehbar ist. Wenn das Proxy-Objekt beispielsweise serialisiert ist, können Sie einenull
Referenz als serialisiertes Ergebnis erhalten, oder wenn eine Methode für das Proxy-Objekt aufgerufen wird, wird eine AusnahmeLazyInitializationException
ausgelöst.Dies bedeutet, dass der Wurf davon
EntityNotFoundException
der Hauptgrund für diegetReference()
Behandlung einer Instanz ist, die nicht in der Datenbank vorhanden ist, da eine Fehlersituation möglicherweise niemals ausgeführt wird, solange die Entität nicht vorhanden ist.EntityManager.find()
hat nicht den Ehrgeiz zu werfen,EntityNotFoundException
wenn die Entität nicht gefunden wird. Sein Verhalten ist sowohl einfach als auch klar. Sie werden nie überrascht sein, da es immer eine geladene Entität odernull
(falls die Entität nicht gefunden wird) zurückgibt, aber niemals eine Entität in Form eines Proxys, die möglicherweise nicht effektiv geladen wird.Sollte
EntityManager.find()
also in den meisten Fällen bevorzugt werden.quelle
Ich bin mit der ausgewählten Antwort nicht einverstanden, und wie davidxxx richtig hervorhob, bietet getReference kein solches Verhalten für dynamische Aktualisierungen ohne Auswahl. Ich habe eine Frage zur Gültigkeit dieser Antwort gestellt, siehe hier - kann nicht aktualisiert werden, ohne select on using setter nach getReference () von JPA im Ruhezustand auszugeben .
Ich habe ehrlich gesagt niemanden gesehen, der diese Funktionalität tatsächlich genutzt hat. IRGENDWO. Und ich verstehe nicht, warum es so positiv ist.
Unabhängig davon, was Sie für ein Proxy-Objekt im Ruhezustand, einen Setter oder einen Getter aufrufen, wird zunächst eine SQL ausgelöst und das Objekt geladen.
Aber dann dachte ich mir, was ist, wenn der JPA-Proxy getReference () diese Funktionalität nicht bietet? Ich kann einfach meinen eigenen Proxy schreiben.
Jetzt können wir alle argumentieren, dass die Auswahl von Primärschlüsseln so schnell ist, wie eine Abfrage nur sein kann, und dass es nicht wirklich etwas ist, große Anstrengungen zu unternehmen, um dies zu vermeiden. Aber für diejenigen von uns, die aus dem einen oder anderen Grund nicht damit umgehen können, ist unten eine Implementierung eines solchen Proxys aufgeführt. Aber bevor Sie die Implementierung sehen, sehen Sie, wie sie verwendet wird und wie einfach sie zu verwenden ist.
VERWENDUNG
Und dies würde die folgende Abfrage auslösen -
und selbst wenn Sie einfügen möchten, können Sie PersistenceService.save (neue Reihenfolge ("a", 2)) ausführen; und es würde einen Einsatz abfeuern, wie es sollte.
IMPLEMENTIERUNG
Fügen Sie dies Ihrer pom.xml hinzu -
Erstellen Sie diese Klasse, um einen dynamischen Proxy zu erstellen.
Erstellen Sie eine Schnittstelle mit allen Methoden -
Erstellen Sie nun einen Interceptor, mit dem Sie diese Methoden auf Ihrem Proxy implementieren können.
Und die Ausnahmeklasse -
Ein Dienst zum Speichern mit diesem Proxy -
quelle