Was ist der richtige Weg, um getrennte Objekte im Ruhezustand wieder anzuhängen?

186

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.

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

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

Stefan Kendall
quelle

Antworten:

181

Es scheint also keine Möglichkeit zu geben, eine veraltete, freistehende Entität in JPA wieder zuzuordnen.

merge() Überträgt den veralteten Status in die Datenbank und überschreibt alle dazwischen liegenden Aktualisierungen.

refresh() kann nicht für eine getrennte Entität aufgerufen werden.

lock() kann nicht für eine getrennte Entität aufgerufen werden, und selbst wenn dies möglich wäre und die Entität erneut verbunden wurde, ist der Aufruf von 'lock' mit dem Argument 'LockMode.NONE', das impliziert, dass Sie sperren, aber nicht sperren, das eingängigste Teil des API-Designs Ich je gesehen habe.

Sie stecken also fest. Es gibt eine detach()Methode, aber nein attach()oder reattach(). Ein offensichtlicher Schritt im Objektlebenszyklus steht Ihnen nicht zur Verfügung.

Gemessen an der Anzahl ähnlicher Fragen zu JPA scheint es, dass JPA, selbst wenn es behauptet, ein kohärentes Modell zu haben, mit Sicherheit nicht dem mentalen Modell der meisten Programmierer entspricht, die verflucht wurden, viele Stunden damit zu verschwenden, zu verstehen, wie man es bekommt JPA, um die einfachsten Dinge zu erledigen und Cache-Verwaltungscode in allen Anwendungen zu erhalten.

Es scheint die einzige Möglichkeit zu sein, Ihre veraltete, getrennte Entität zu verwerfen und eine Suchabfrage mit derselben ID durchzuführen, die den L2 oder die DB trifft.

Mik

mikhailfranco
quelle
1
Ich frage mich, ob es einen Grund gibt, warum die JPA-Spezifikation 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.
FGreg
11
Dies ist definitiv nicht genau. Von JPwH: *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.2
cwash
Persistente Objekte funktionieren hervorragend. Das Dirty-Flag wird basierend auf dem Delta zwischen der anfänglichen Last und den Werten zur Zeit von flush () gesetzt. Freistehende Objekte benötigen und verfügen derzeit nicht über diese Funktionalität. Der Ruhezustand besteht darin, einen zusätzlichen Hash / eine zusätzliche ID für getrennte Objekte hinzuzufügen. Halten Sie eine Momentaufnahme des letzten Status des getrennten Objekts verfügbar, genau wie bei persistenten Objekten. So können sie den gesamten vorhandenen Code nutzen und ihn für getrennte Objekte verwenden. Auf diese Weise werden wir, wie @mikhailfranco feststellte, "den veralteten Zustand nicht in die Datenbank übertragen und dazwischenliegende Aktualisierungen überschreiben"
Tom
2
Laut dem Hibernate kann Javadoc (aber nicht JPA) lock(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/14379
Seanf
Die Sperre hat bei mir nicht funktioniert: java.lang.IllegalArgumentException: Entität nicht im Persistenzkontext bei org.hibernate.internal.SessionImpl.lock (SessionImpl.java:3491) bei org.hibernate.internal.SessionImpl.lock (SessionImpl. Java: 3482) bei com.github.vok.framework.DisableTransactionControlEMDelegate.lock (DB.kt)
Martin Vysny
32

Alle 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

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

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 () ...

Steve Ebersole
quelle
3
Dies ist die richtige Antwort auf diese Frage - Fall abgeschlossen!
cwash
2
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.
DJJJ
Wenn als Session.contains(Object)Referenz überprüft wird, dass eine andere Entität dieselbe Zeile in der Sitzung darstellt, wird false zurückgegeben und aktualisiert.
AxelWass
19

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.

cwash
quelle
5
Von nahtframework.org : "Die aktive Entwicklung von Seam 3 wurde von Red Hat gestoppt." Der Link "Dieses Dokument von Seam" ist ebenfalls tot.
Badbishop
14

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.

session.lock(entity, LockMode.NONE);

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

entity = session.get(entity.getClass(), entity.getId());
John Rizzo
quelle
2
Ich möchte eine Entität erneut mit der Sitzung verknüpfen. Leider 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?
dma_k
1
Tatsächlich hatte ich nicht ganz recht. Mit lock () wird Ihre Entität erneut verbunden, nicht jedoch die anderen daran gebundenen Entitäten. Wenn Sie also entity.getOtherEntity (). GetYetAnotherEntity () ausführen, liegt möglicherweise eine LazyInit-Ausnahme vor. Der einzige Weg, den ich zu überwinden weiß, ist find zu verwenden. entity = em.find (entity.getClass (), entity.getId ();
John Rizzo
Es gibt keine Session.find()API-Methode. Vielleicht meinst du Session.load(Object object, Serializable id).
dma_k
11

Da dies eine sehr häufige Frage ist, habe ich diesen Artikel geschrieben , auf dem diese Antwort basiert.

Entitätszustände

JPA definiert die folgenden Entitätszustände:

Neu (vorübergehend)

Ein neu erstelltes Objekt, das noch nie einem Ruhezustand Session(aka Persistence 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#persistMethode 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 synchronisiertSession 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:

Übergänge des JPA-Entitätsstatus

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, mergekönnen Sie außerdem eine getrennte Entität mithilfe der Aktualisierungsmethoden erneut einer aktiven Hibernate-Sitzung zuordnen, wie im folgenden Diagramm dargestellt:

Zustandsübergänge im Ruhezustand

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 BookEntität beibehalten, und jetzt wird die Entität getrennt, da die EntityManagerzum Bestehen der Entität verwendete Entität geschlossen wurde:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

Während sich die Entität im getrennten Zustand befindet, ändern wir sie wie folgt:

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

Jetzt möchten wir die Änderungen an die Datenbank weitergeben, damit wir die mergeMethode aufrufen können :

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

Und Hibernate wird die folgenden SQL-Anweisungen ausführen:

SELECT
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM
    book b
WHERE
    b.id = 1

-- Merging the Book entity

UPDATE
    book
SET
    author = 'Vlad Mihalcea',
    isbn = '978-9730228236',
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE
    id = 1

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 Persistenzkontextsflush wird ein UPDATE generiert, wenn der Dirty-Checking-Mechanismus feststellt, dass sich die verwaltete Entität geändert hat.

Bei Verwendung mergebleibt die Instanz des getrennten Objekts auch nach dem Zusammenführungsvorgang weiterhin getrennt.

Erneutes Anhängen einer getrennten Entität

Ruhezustand, aber nicht JPA unterstützt das erneute Anhängen über die updateMethode.

Ein Ruhezustand Sessionkann 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 die BookEntität beibehalten haben und dass wir sie geändert haben, als sich die Entität im getrennten Zustand befand:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

Wir können die abgetrennte Entität wie folgt wieder anbringen:

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

Und Hibernate führt die folgende SQL-Anweisung aus:

-- Updating the Book entity

UPDATE
    book
SET
    author = 'Vlad Mihalcea',
    isbn = '978-9730228236',
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE
    id = 1

Die updateMethode erfordert, dass Sie in unwrapden EntityManagerRuhezustand wechseln Session.

Im Gegensatz dazu mergewird 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 @SelectBeforeUpdateAnnotation Hibernate verwenden, die eine SELECT-Anweisung auslöst, die den geladenen Status abruft, der dann vom Dirty-Checking-Mechanismus verwendet wird.

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

Achten Sie auf die NonUniqueObjectException

Ein Problem, das auftreten kann, updatebesteht darin, dass der Persistenzkontext bereits eine Entitätsreferenz mit derselben ID und demselben Typ wie im folgenden Beispiel enthält:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

try {
    doInJPA(entityManager -> {
        Book book = entityManager.find(
            Book.class,
            _book.getId()
        );

        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(_book);
    });
} catch (NonUniqueObjectException e) {
    LOGGER.error(
        "The Persistence Context cannot hold " +
        "two representations of the same entity",
        e
    );
}

Wenn Sie den obigen Testfall ausführen, wird Hibernate a auslösen, NonUniqueObjectExceptionda der zweite EntityManagerbereits eine BookEntität mit derselben Kennung enthält wie die, an die wir übergeben update, und der Persistenzkontext nicht zwei Darstellungen derselben Entität enthalten kann.

org.hibernate.NonUniqueObjectException:
    A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)

Fazit

Die mergeMethode ist vorzuziehen, wenn Sie optimistische Sperren verwenden, um verlorene Aktualisierungen zu vermeiden. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .

Dies updateist gut für Stapelaktualisierungen, da es die zusätzliche SELECT-Anweisung verhindern kann, die durch die mergeOperation generiert wird , wodurch die Ausführungszeit für Stapelaktualisierungen verkürzt wird.

Vlad Mihalcea
quelle
Gute Antwort. Ich habe mich allerdings über die @SelectBeforeUpdateAnmerkung gewundert. Wann wird die Auswahl ausgelöst? Beim Aufrufen update, 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)?
Andronicus
Das @SelectBeforeUpdatelöst SELECT während der Persistenzkontextoperation aus flush. Weitere Informationen finden Sie in der getDatabaseSnapshotMethode inDefaultFlushEntityEventListener .
Vlad Mihalcea
10

Ich ging zurück zum JavaDoc org.hibernate.Sessionund fand Folgendes:

Transient Instanzen persistent sein kann durch den Aufruf gemacht save(), persist()oder saveOrUpdate(). Persistente Instanzen können durch Aufrufen vorübergehend gemacht werden delete(). Jede von einer get()oder load()-Methode zurückgegebene Instanz ist persistent. Freistehendes Instanzen persistent sein kann durch den Aufruf gemacht update(), saveOrUpdate(), lock()oder replicate(). Der Status einer vorübergehenden oder getrennten Instanz kann durch Aufrufen auch als neue persistente Instanz persistent gemacht werden merge().

So update(), saveOrUpdate(), lock(), replicate()und merge()sind die Kandidaten - Optionen.

update(): Löst eine Ausnahme aus, wenn eine persistente Instanz mit derselben Kennung vorhanden ist.

saveOrUpdate(): Entweder speichern oder aktualisieren

lock(): Veraltet

replicate(): 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.

Amitabha Roy
quelle
7

Ich habe es in C # mit NHibernate so gemacht, aber in Java sollte es genauso funktionieren:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

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 equalsMethode, die anhand einer Referenz verglichen wird, wenn sie nicht überschrieben wird. Mit dieser equalsMethode funktioniert es ohne Ausnahmen:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}
Verena Haunschmid
quelle
4

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.

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

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.

djmj
quelle
3

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:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}
WhoopP
quelle
2

Leider können anscheinend noch keine Kommentare hinzugefügt werden.

Verwenden von Hibernate 3.5.0-Final

Während die Session#lockMethode dies veraltete, schlägt der Javadoc vor , zu verwenden Session#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

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

Erste Tests legen nahe, dass es ein Genuss ist.

Gwaptiva
quelle
2

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:

Object obj = em.find(obj.getClass(), id);

und optional einen zweiten Schritt (um Caches ungültig zu machen):

em.refresh(obj)
Hartmut P.
quelle
1

Versuchen Sie getHibernateTemplate (). replicate (Entität, ReplicationMode.LATEST_VERSION)

Pavitar Singh
quelle
1

In der ursprünglichen Post, gibt es zwei Methoden, update(obj)und merge(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 rufen update(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:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}
John DeRegnaucourt
quelle
2
enthält () prüft Vergleiche anhand von Referenzen, aber Ruhezustandsfunktionen arbeiten nach Datenbank-ID. session.merge wird in Ihrem Code niemals aufgerufen.
Verena Haunschmid
1

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.

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);
Ryuku
quelle
0

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.

Käse
quelle
0

Eigentum hibernate.allow_refresh_detached_entityhat 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

Radeck
quelle
-6
try getHibernateTemplate().saveOrUpdate()
Ben Hammond
quelle