it-swarm.com.de

Was sind die Unterschiede zwischen den verschiedenen Speichermethoden im Ruhezustand?

Hibernate verfügt über eine Handvoll Methoden, mit denen Sie Ihr Objekt auf die eine oder andere Weise in die Datenbank kopieren können. Was sind die Unterschiede zwischen ihnen, wann welche zu verwenden sind und warum gibt es nicht nur eine intelligente Methode, die weiß, wann welche zu verwenden ist?

Die Methoden, die ich bisher identifiziert habe, sind:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
196
Henrik Paul

Hier ist mein Verständnis der Methoden. Hauptsächlich basieren diese auf dem API , obwohl ich nicht alle in der Praxis verwende.

saveOrUpdate Ruft abhängig von einigen Überprüfungen entweder save oder update auf. Z.B. Ist keine Kennung vorhanden, wird save aufgerufen. Andernfalls wird update aufgerufen.

save Bewahrt eine Entität auf. Weist eine Kennung zu, wenn keine existiert. In diesem Fall wird im Wesentlichen ein Update durchgeführt. Gibt die generierte ID der Entität zurück.

update Versucht, die Entität unter Verwendung eines vorhandenen Bezeichners beizubehalten. Wenn kein Bezeichner vorhanden ist, wird meiner Meinung nach eine Ausnahme ausgelöst.

saveOrUpdateCopy Dies ist veraltet und sollte nicht mehr verwendet werden. Stattdessen gibt es ...

verschmelzen Jetzt fängt mein Wissen an, ins Wanken zu geraten. Wichtig ist hier der Unterschied zwischen vorübergehenden, getrennten und dauerhaften Entitäten. Weitere Informationen zu den Objektzuständen finden Sie unter siehe hier . Mit save & update haben Sie es mit persistenten Objekten zu tun. Sie sind mit einer Sitzung verknüpft, damit der Ruhezustand weiß, was sich geändert hat. Wenn Sie jedoch ein vorübergehendes Objekt haben, ist keine Sitzung beteiligt. In diesen Fällen müssen Sie die Zusammenführung für Aktualisierungen verwenden und zum Speichern beibehalten.

persist Wie oben erwähnt, wird dies für transiente Objekte verwendet. Die generierte ID wird nicht zurückgegeben.

117
Lee Theobald
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    METHOD    ║            TRANSIENT          ║            DETACHED            ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id if doesn't      ║   sets new id even if object   ║
║    save()    ║     exist, persists to db,    ║    already has it, persists    ║
║              ║    returns attached object    ║ to DB, returns attached object ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id on object       ║             throws             ║
║   persist()  ║     persists object to DB     ║       PersistenceException     ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║   update()   ║           Exception           ║     persists and reattaches    ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║  copy the state of object in  ║    copy the state of obj in    ║
║    merge()   ║     DB, doesn't attach it,    ║      DB, doesn't attach it,    ║
║              ║    returns attached object    ║     returns attached object    ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║saveOrUpdate()║           as save()           ║            as update()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
114
Sergii Shevchyk
  • Im Hibernate Forum finden Sie eine Erklärung der subtilen Unterschiede zwischen persist und save. Es sieht so aus, als ob der Unterschied darin besteht, wann die INSERT-Anweisung letztendlich ausgeführt wird. Da save den Bezeichner zurückgibt, muss die INSERT-Anweisung sofort ausgeführt werden, unabhängig vom Status der Transaktion (was im Allgemeinen eine schlechte Sache ist). Persist führt keine Anweisungen außerhalb der aktuell ausgeführten Transaktion aus, nur um den Bezeichner zuzuweisen. Save/Persist beide arbeiten an transienten Instanzen, dh Instanzen, denen noch keine Kennung zugeordnet ist und die als solche nicht in der DB gespeichert sind.

  • Update und Merge arbeiten beide mit getrennten Instanzen, dh Instanzen, die einen entsprechenden Eintrag in haben die Datenbank, die jedoch derzeit keiner Sitzung zugeordnet ist (oder von dieser verwaltet wird). Der Unterschied besteht darin, was mit der Instanz passiert, die an die Funktion übergeben wird. update versucht, die Instanz erneut zuzuordnen. Dies bedeutet, dass derzeit möglicherweise keine andere Instanz der persistenten Entität mit der Sitzung verbunden ist. Andernfalls wird eine Ausnahme ausgelöst. merge kopiert jedoch nur alle Werte in eine persistente Instanz in der Sitzung (die geladen wird, wenn sie derzeit nicht geladen ist). Das Eingabeobjekt wird nicht verändert. merge ist also allgemeiner als update , kann aber mehr Ressourcen verbrauchen.

65
jrudolph

Dieser Link erklärt in guter Weise:

http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

Wir alle haben Probleme, die nur selten auftreten, sodass wir wissen, dass wir sie gelöst haben, wenn wir sie wieder sehen. Wir können uns jedoch nicht erinnern, wie.

Die NonUniqueObjectException, die bei der Verwendung von Session.saveOrUpdate () im Ruhezustand ausgelöst wird, ist eine meiner Ausnahmen. Ich füge einer komplexen Anwendung neue Funktionen hinzu. Alle meine Unit-Tests funktionieren einwandfrei. Beim Testen der Benutzeroberfläche, während ich versuche, ein Objekt zu speichern, wird eine Ausnahme mit der Meldung angezeigt, dass der Sitzung bereits ein anderes Objekt mit demselben Bezeichnerwert zugeordnet wurde. Hier ist ein Beispielcode aus Java Persistence mit Hibernate.

            Session session = sessionFactory1.openSession();
            Transaction tx = session.beginTransaction();
            Item item = (Item) session.get(Item.class, new Long(1234));
            tx.commit();
            session.close(); // end of first session, item is detached

            item.getId(); // The database identity is "1234"
            item.setDescription("my new description");
            Session session2 = sessionFactory.openSession();
            Transaction tx2 = session2.beginTransaction();
            Item item2 = (Item) session2.get(Item.class, new Long(1234));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

Um die Ursache dieser Ausnahme zu verstehen, ist es wichtig, die getrennten Objekte zu verstehen und zu verstehen, was passiert, wenn Sie saveOrUpdate () (oder nur update ()) für ein getrenntes Objekt aufrufen.

Wenn wir eine einzelne Ruhezustandssitzung schließen, werden die permanenten Objekte, mit denen wir arbeiten, getrennt. Dies bedeutet, dass sich die Daten noch im Speicher der Anwendung befinden, der Ruhezustand jedoch nicht mehr für die Nachverfolgung von Änderungen an den Objekten verantwortlich ist.

Wenn wir dann unser getrenntes Objekt ändern und es aktualisieren möchten, müssen wir das Objekt erneut anfügen. Während dieses Wiederherstellungsvorgangs prüft Hibernate, ob weitere Kopien desselben Objekts vorhanden sind. Wenn es welche findet, muss es uns mitteilen, dass es nicht mehr weiß, was die "echte" Kopie ist. Möglicherweise wurden andere Änderungen an den anderen Kopien vorgenommen, von denen wir erwarten, dass sie gespeichert werden. Hibernate weiß jedoch nichts darüber, da sie zu diesem Zeitpunkt nicht verwaltet wurden.

Anstatt möglicherweise fehlerhafte Daten zu speichern, teilt uns Hibernate das Problem über die NonUniqueObjectException mit.

Also, was sollen wir tun? In Ruhezustand 3 haben wir merge () (in Ruhezustand 2 verwenden Sie saveOrUpdateCopy ()). Diese Methode erzwingt, dass Hibernate alle Änderungen von anderen getrennten Instanzen auf die zu speichernde Instanz kopiert, und führt somit alle Änderungen im Speicher vor dem Speichern zusammen.

        Session session = sessionFactory1.openSession();
        Transaction tx = session.beginTransaction();
        Item item = (Item) session.get(Item.class, new Long(1234));
        tx.commit();
        session.close(); // end of first session, item is detached

        item.getId(); // The database identity is "1234"
        item.setDescription("my new description");
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Item item2 = (Item) session2.get(Item.class, new Long(1234));
        Item item3 = session2.merge(item); // Success!
        tx2.commit();
        session2.close();

Beachten Sie, dass beim Zusammenführen ein Verweis auf die neu aktualisierte Version der Instanz zurückgegeben wird. Es wird kein Element wieder der Sitzung zugeordnet. Wenn Sie beispielsweise Gleichheit testen (item == item3), wird in diesem Fall false zurückgegeben. Sie werden wahrscheinlich von diesem Punkt an mit item3 arbeiten wollen.

Es ist auch wichtig zu beachten, dass die Java-Persistenz-API (JPA) kein Konzept für getrennte und erneut verbundene Objekte hat und EntityManager.persist () und EntityManager.merge () verwendet.

Ich habe im Allgemeinen festgestellt, dass saveOrUpdate () bei Verwendung des Ruhezustands normalerweise für meine Anforderungen ausreicht. Normalerweise muss ich nur Zusammenführen verwenden, wenn ich Objekte habe, die auf Objekte desselben Typs verweisen können. Die Ursache für die Ausnahme lag zuletzt im Code, der bestätigte, dass die Referenz nicht rekursiv war. Ich habe im Rahmen der Überprüfung dasselbe Objekt in meine Sitzung geladen, wodurch der Fehler verursacht wurde.

Wo sind Sie auf dieses Problem gestoßen? Hat die Zusammenführung für Sie funktioniert oder brauchten Sie eine andere Lösung? Verwenden Sie die Option "Zusammenführen" lieber immer oder nur nach Bedarf für bestimmte Fälle

12
HakunaMatata

Ich habe ein gutes Beispiel gefunden, das die Unterschiede zwischen allen Methoden zum Speichern des Ruhezustands zeigt:

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

Kurz gesagt, laut dem obigen Link:

save ()

  • Wir können diese Methode außerhalb einer Transaktion aufrufen. Wenn wir dies ohne Transaktion verwenden und zwischen Entitäten kaskadieren, wird nur die primäre Entität gespeichert, es sei denn, wir leeren die Sitzung.
  • Wenn also andere Objekte vom primären Objekt zugeordnet sind, werden sie zum Zeitpunkt des Transaktions-Commits oder beim Leeren der Sitzung gespeichert.

persist ()

  • Es ähnelt der Verwendung von save () in einer Transaktion, ist also sicher und kümmert sich um alle kaskadierten Objekte.

saveOrUpdate ()

  • Kann mit oder ohne Transaktion verwendet werden, und genau wie save () werden zugeordnete Entitäten nicht gespeichert, wenn sie ohne Transaktion verwendet werden; wir leeren die Sitzung.

  • Führt zu Einfüge- oder Aktualisierungsabfragen basierend auf den bereitgestellten Daten. Wenn die Daten in der Datenbank vorhanden sind, wird eine Aktualisierungsabfrage ausgeführt.

update ()

  • Die Aktualisierung im Ruhezustand sollte verwendet werden, wenn bekannt ist, dass nur die Entitätsinformationen aktualisiert werden. Durch diesen Vorgang wird das Entitätsobjekt zum permanenten Kontext hinzugefügt und weitere Änderungen werden nachverfolgt und gespeichert, wenn die Transaktion festgeschrieben wird.
  • Wenn wir also nach dem Aufruf von update Werte in der Entität festlegen, werden diese aktualisiert, wenn die Transaktion festgeschrieben wird.

merge ()

  • Die Zusammenführung im Ruhezustand kann zum Aktualisieren vorhandener Werte verwendet werden. Diese Methode erstellt jedoch eine Kopie des übergebenen Entitätsobjekts und gibt sie zurück. Das zurückgegebene Objekt ist Teil des permanenten Kontexts und wird auf Änderungen überwacht. Übergebenes Objekt wird nicht verfolgt. Dies ist der Hauptunterschied zwischen merge () und allen anderen Methoden.

Auch für praktische Beispiele von all diesen verweisen wir auf den Link, den ich oben erwähnte. Es zeigt Beispiele für all diese verschiedenen Methoden.

5
OutOfMind

Tatsächlich hängt der Unterschied zwischen den Methoden save() und persist() im Ruhezustand von der verwendeten Generatorklasse ab.

Wenn unsere Generatorklasse zugewiesen ist, gibt es keinen Unterschied zwischen den Methoden save() und persist(). Da "zugewiesen" für den Generator bedeutet, dass wir als Programmierer den Primärschlüsselwert angeben müssen, um ihn in der Datenbank zu speichern Wenn Sie den Ruhezustand selbst aktivieren, wird der Primärschlüssel-ID-Wert der Datenbank zugewiesen [außer dem zugewiesenen Generator, der Ruhezustand wird nur verwendet, um den Primärschlüssel-ID-Wert zu berücksichtigen]. Wenn Sie also in diesem Fall save() oder persist() aufrufen, wird die Methode eingefügt Normalerweise in die Datenbank aufnehmen Aber wie gesagt, die Methode save() kann den Primärschlüssel-ID-Wert zurückgeben, der durch den Ruhezustand generiert wird, und wir können ihn durch sehen

long s = session.save(k);

In diesem Fall gibt persist() dem Client niemals einen Wert zurück.

5
Hari Krishna

Beachten Sie, dass beim Aufrufen einer Aktualisierung für ein getrenntes Objekt immer eine Aktualisierung in der Datenbank durchgeführt wird, unabhängig davon, ob Sie das Objekt geändert haben oder nicht. Wenn es nicht das ist, was Sie wollen, sollten Sie Session.lock () mit LockMode.None verwenden.

Sie sollten update nur aufrufen, wenn das Objekt außerhalb des Bereichs Ihrer aktuellen Sitzung geändert wurde (im getrennten Modus).

2
bernardn

Wie ich in diesem Artikel erklärt habe, sollten Sie die JPA-Methoden die meiste Zeit bevorzugen und update für Stapelverarbeitungsaufgaben.

Eine JPA- oder Ruhezustand-Entität kann sich in einem der folgenden vier Zustände befinden:

  • Transient (neu)
  • Verwaltet (dauerhaft)
  • Freistehend
  • Entfernt (gelöscht)

Der Übergang von einem Zustand in den anderen erfolgt über die EntityManager- oder Session-Methoden.

Beispielsweise bietet die JPA EntityManager die folgenden Methoden für den Statusübergang von Entitäten.

enter image description here

Der Ruhezustand Session implementiert alle JPA-Methoden EntityManager und stellt einige zusätzliche Methoden für den Entitätsstatusübergang bereit, z. B. save, saveOrUpdate und update.

enter image description here

Fortdauern

Um den Status einer Entität von "Transient" (neu) in "Verwaltet" (dauerhaft) zu ändern, können Sie die Methode "persist" verwenden, die von der JPA "EntityManager" angeboten wird, die auch vom Ruhezustand "Session" geerbt wird.

Die Methode persist löst eine PersistEvent aus, die vom Ereignis-Listener DefaultPersistEventListener Hibernate verarbeitet wird.

Daher bei der Ausführung des folgenden Testfalls:

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

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

Hibernate generiert die folgenden SQL-Anweisungen:

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Beachten Sie, dass id zugewiesen wird, bevor die Entität Book dem aktuellen Persistenzkontext hinzugefügt wird. Dies ist erforderlich, da die verwalteten Entitäten in einer Map-Struktur gespeichert sind, in der der Schlüssel aus dem Entitätstyp und seinem Bezeichner besteht und der Wert die Entitätsreferenz ist. Dies ist der Grund, warum JPA EntityManager und Hibernate Session als Cache der ersten Ebene bezeichnet werden.

Wenn Sie persist aufrufen, wird die Entität nur an den aktuell ausgeführten Persistenzkontext angehängt, und das INSERT kann verschoben werden, bis flush aufgerufen wird.

Die einzige Ausnahme ist der IDENTITY-Generator , der das INSERT sofort auslöst, da nur so die Entity-ID ermittelt werden kann. Aus diesem Grund kann Hibernate keine Batch-Einfügungen für Entitäten durchführen, die den IDENTITY-Generator verwenden. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .

Speichern

Die Hibernate-spezifische save-Methode ist älter als JPA und seit Beginn des Hibernate-Projekts verfügbar.

Die Methode save löst eine SaveOrUpdateEvent aus, die vom Ereignis-Listener DefaultSaveOrUpdateEventListener Hibernate verarbeitet wird. Daher entspricht die Methode save den Methoden update und saveOrUpdate.

Betrachten Sie den folgenden Testfall, um zu sehen, wie die Methode save funktioniert:

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

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

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

Beim Ausführen des obigen Testfalls generiert Hibernate die folgenden SQL-Anweisungen:

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Wie Sie sehen, ist das Ergebnis identisch mit dem Methodenaufruf persist. Im Gegensatz zu persist gibt die save-Methode jedoch die Entitäts-ID zurück.

Weitere Informationen finden Sie unter dieser Artikel .

Aktualisieren

Die Hibernate-spezifische update-Methode umgeht den Dirty-Checking-Mechanismus und erzwingt eine Entity-Aktualisierung zum Flush-Zeitpunkt.

Die Methode update löst eine SaveOrUpdateEvent aus, die vom Ereignis-Listener DefaultSaveOrUpdateEventListener Hibernate verarbeitet wird. Daher entspricht die Methode update den Methoden save und saveOrUpdate.

Um zu sehen, wie die update-Methode funktioniert, betrachten Sie das folgende Beispiel, in dem eine Book-Entität in einer Transaktion beibehalten wird. Anschließend wird sie geändert, während sich die Entität im getrennten Zustand befindet, und das SQL-UPDATE wird mithilfe des update-Methodenaufrufs erzwungen.

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

    entityManager.persist(book);

    return book;
});

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

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

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

    session.update(_book);

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

Beim Ausführen des obigen Testfalls generiert Hibernate die folgenden SQL-Anweisungen:

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

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

Beachten Sie, dass UPDATE während des Löschens des Persistenzkontextes kurz vor dem Festschreiben ausgeführt wird. Aus diesem Grund wird die Meldung Updating the Book entity Zuerst protokolliert.

Verwenden Sie @SelectBeforeUpdate, Um unnötige Aktualisierungen zu vermeiden

Jetzt wird UPDATE immer ausgeführt, auch wenn die Entität im getrennten Zustand nicht geändert wurde. Um dies zu verhindern, können Sie die Annotation @SelectBeforeUpdate Hibernate verwenden, die eine Anweisung SELECT auslöst, die loaded state Abruft und dann vom Dirty Checking-Mechanismus verwendet wird.

Wenn wir also die Entität Book mit der Annotation @SelectBeforeUpdate Versehen:

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

    //Code omitted for brevity
}

Führen Sie den folgenden Testfall aus:

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

    entityManager.persist(book);

    return book;
});

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

    session.update(_book);
});

Hibernate führt die folgenden SQL-Anweisungen aus:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

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

Beachten Sie, dass diesmal kein UPDATE ausgeführt wird, da der Mechanismus zur Überprüfung des Ruhezustands festgestellt hat, dass die Entität nicht geändert wurde.

SaveOrUpdate

Die für den Ruhezustand spezifische Methode saveOrUpdate ist nur ein Alias ​​für save und update.

Die Methode saveOrUpdate löst eine SaveOrUpdateEvent aus, die vom Ereignis-Listener DefaultSaveOrUpdateEventListener Hibernate verarbeitet wird. Daher entspricht die Methode update den Methoden save und saveOrUpdate.

Jetzt können Sie saveOrUpdate verwenden, wenn Sie eine Entität beibehalten oder ein UPDATE erzwingen möchten, wie im folgenden Beispiel dargestellt.

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

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

Vorsicht vor den NonUniqueObjectException

Ein Problem, das bei save, update und saveOrUpdate auftreten kann, besteht darin, dass der Persistenzkontext bereits einen Entitätsverweis 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 ein NonUniqueObjectException auslösen, da das zweite EntityManager bereits eine Book -Entität mit demselben Bezeichner enthält, den wir an update übergeben, und der Persistenzkontext kann nicht zwei Entitäten derselben enthalten.

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)

Verschmelzen

Um NonUniqueObjectException zu vermeiden, müssen Sie auch die Methode merge verwenden, die von JPA EntityManager angeboten und von Hibernate Session geerbt wird.

Wie in diesem Artikel erläutert, ruft merge einen neuen Entitätsschnappschuss aus der Datenbank ab, wenn im Persistenzkontext kein Entitätsverweis gefunden wird, und kopiert den Status der getrennten Entität, der an die Methode merge übergeben wurde.

Die Methode merge löst eine MergeEvent aus, die vom Ereignis-Listener DefaultMergeEventListener Hibernate verarbeitet wird.

Um zu sehen, wie die merge-Methode funktioniert, betrachten Sie das folgende Beispiel, das eine Book-Entität in einer Transaktion beibehält, sie dann ändert, während sich die Entität im getrennten Zustand befindet, und die getrennte Entität an merge in einem Subsequenz-Persistenzkontext übergibt.

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

    entityManager.persist(book);

    return book;
});

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

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

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

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

    assertFalse(book == _book);
});

Beim Ausführen des obigen Testfalls führte Hibernate die folgenden SQL-Anweisungen aus:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

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

Beachten Sie, dass sich die von merge zurückgegebene Entitätsreferenz von der getrennten unterscheidet, die wir an die merge-Methode übergeben haben.

Obwohl Sie JPA merge vorziehen sollten, wenn Sie den Status der getrennten Entität kopieren, kann das zusätzliche SELECT problematisch sein, wenn Sie eine Stapelverarbeitungsaufgabe ausführen.

Aus diesem Grund sollten Sie update vorziehen, wenn Sie sicher sind, dass dem aktuell ausgeführten Persistenzkontext kein Entitätsverweis zugeordnet ist und die getrennte Entität geändert wurde.

Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .

Fazit

Um eine Entität beizubehalten, sollten Sie die JPA-Methode persist verwenden. Zum Kopieren des Status der getrennten Entität sollte merge bevorzugt werden. Die Methode update ist nur für Stapelverarbeitungsaufgaben nützlich. Die save und saveOrUpdate sind nur Aliase für update, und Sie sollten sie wahrscheinlich überhaupt nicht verwenden.

Einige Entwickler rufen save auf, auch wenn die Entität bereits verwaltet wird. Dies ist jedoch ein Fehler und löst ein redundantes Ereignis aus, da für verwaltete Entitäten das UPDATE automatisch zum Zeitpunkt des Löschens des Persistenzkontexts behandelt wird.

Weitere Informationen finden Sie unter dieser Artikel .

2
Vlad Mihalcea

Keine der folgenden Antworten ist richtig. Alle diese Methoden scheinen einfach gleich zu sein, aber in der Praxis machen sie absolut verschiedene Dinge. Es ist schwer, kurze Kommentare abzugeben. Besser, Sie geben einen Link zur vollständigen Dokumentation dieser Methoden an: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

1
Anton Popovich

Keine der obigen Antworten ist vollständig. Obwohl die Antwort von Leo Theobald die nächste Antwort ist.

Der grundlegende Punkt ist, wie der Ruhezustand mit Zuständen von Entitäten umgeht und wie er mit ihnen umgeht, wenn sich der Zustand ändert. Alles muss in Bezug auf Flushes und Commits gesehen werden, die anscheinend jeder völlig ignoriert hat.

VERWENDEN SIE NIEMALS DIE SPEICHERMETHODE VON HIBERNATE. VERGESSEN SIE, DASS ES NOCH IM HIBERNATE GIBT!

Fortbestehen

Wie alle erklärten, überführt Persist eine Entität im Grunde genommen vom Status "Transient" in den Status "Managed". Zu diesem Zeitpunkt kann ein Slush oder ein Commit eine insert-Anweisung erstellen. Die Entität bleibt jedoch weiterhin im Status "Verwaltet". Das ändert sich mit Flush nicht.

Wenn Sie zu diesem Zeitpunkt erneut "beharren", wird sich nichts ändern. Und es wird keine Rettung mehr geben, wenn wir versuchen, eine beständige Entität aufrechtzuerhalten.

Der Spaß beginnt, wenn wir versuchen, die Entität zu vertreiben.

Ein Evict ist eine spezielle Funktion von Hibernate, mit der die Entität von "Managed" auf "Detached" umgestellt wird. Wir können keine Persistenz für eine getrennte Entität aufrufen. Wenn wir das tun, löst Hibernate eine Ausnahme aus und die gesamte Transaktion wird beim Festschreiben zurückgesetzt.

Merge vs Update

Dies sind 2 interessante Funktionen, die verschiedene Dinge tun, wenn sie auf unterschiedliche Weise behandelt werden. Beide versuchen, die Entität vom Status "Getrennt" in den Status "Verwaltet" zu überführen. Aber anders machen.

Verstehen Sie eine Tatsache, dass "Getrennt" eine Art "Offline" -Zustand bedeutet. und verwaltet bedeutet "Online" -Zustand.

Beachten Sie den folgenden Code:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

Wann machst du das? Was denkst du wird passieren? Wenn Sie sagten, dies würde eine Ausnahme auslösen, haben Sie Recht. Dies löst eine Ausnahme aus, da die Zusammenführung für das Entitätsobjekt ausgeführt wurde, das im getrennten Zustand ist. Es ändert aber nichts am Objektzustand.

Hinter den Kulissen löst das Zusammenführen eine Auswahlabfrage aus und gibt im Grunde eine Kopie der Entität zurück, die sich im angehängten Zustand befindet. Beachten Sie den folgenden Code:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

Das obige Beispiel funktioniert, da die Zusammenführung eine neue Entität in den Kontext gebracht hat, der sich im dauerhaften Zustand befindet.

Bei der Anwendung mit Update funktioniert dasselbe problemlos, da das Update keine Kopie einer Entität wie "Zusammenführen" enthält.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

Gleichzeitig können wir im Debug-Trace sehen, dass Update die SQL-Abfrage "Select Like Merge" nicht ausgelöst hat.

löschen

Im obigen Beispiel habe ich delete verwendet, ohne von delete zu sprechen. Durch Löschen wird die Entität grundsätzlich vom verwalteten Zustand in den Zustand "entfernt" versetzt. Und wenn geleert oder festgeschrieben, wird ein Löschbefehl zum Speichern ausgegeben.

Es ist jedoch möglich, die Entität mithilfe der Persist-Methode aus dem Status "entfernt" in den Status "verwaltet" zurückzusetzen.

Hoffe, die obige Erklärung hat alle Zweifel geklärt.

0
Bharat Raj