Was sind die Unterschiede zwischen den verschiedenen Speichermethoden im Ruhezustand?

199

Hibernate verfügt über eine Handvoll Methoden, mit denen Sie Ihr Objekt auf die eine oder andere Weise in die Datenbank aufnehmen 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 was zu verwenden ist?

Die Methoden, die ich bisher identifiziert habe, sind:

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

Antworten:

117

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

saveOrUpdate Ruft abhängig von einigen Überprüfungen entweder Speichern oder Aktualisieren auf. Wenn beispielsweise keine Kennung vorhanden ist, wird save aufgerufen. Andernfalls wird das Update aufgerufen.

Speichern Behält eine Entität bei. Weist eine Kennung zu, wenn keine vorhanden ist. Wenn ja, wird im Wesentlichen ein Update durchgeführt. Gibt die generierte ID der Entität zurück.

update Versucht, die Entität unter Verwendung einer vorhandenen Kennung beizubehalten. Wenn keine Kennung vorhanden ist, wird meiner Meinung nach eine Ausnahme ausgelöst.

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

verschmelzen Hier beginnt mein Wissen ins Stocken zu geraten. Das Wichtigste dabei ist der Unterschied zwischen vorübergehenden, getrennten und beständigen Entitäten. Weitere Informationen zu den Objektzuständen finden Sie hier . Mit Speichern und Aktualisieren 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 Merge für Updates verwenden und zum Speichern beibehalten.

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

Lee Theobald
quelle
22
Ich würde dies gerne als Antwort akzeptieren, aber eines ist noch unklar: Da save () auf update () zurückgreift, wie unterscheidet es sich in der Praxis von saveOrUpdate (), wenn ein solches Element vorhanden ist?
Henrik Paul
Wo ist angegeben, dass das Speichern auf getrennten Instanzen funktionieren würde?
Jrudolph
2
Wenn Ihre Beschreibung von Zusammenführen / Fortbestehen nur für vorübergehende Objekte wichtig ist, ist dies sehr sinnvoll und passt zur Verwendung des Ruhezustands. Beachten Sie auch, dass eine Zusammenführung im Vergleich zu einem Update häufig Leistungseinschränkungen aufweist, da sie anscheinend zusätzliche Abrufvorgänge für Integritätsprüfungen ausführt.
Martin Dale Lyness
1
Die Antwort von jrudolph unten ist genauer.
Azerole
2
Wenn der Ruhezustand wahrscheinlich weiß, in welchem ​​Zustand sich ein Objekt befindet, warum müssen wir dies beim Schreiben des Programms manuell tun. Es sollte nur eine Speichermethode geben.
Masterxilo
116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    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()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
Sergii Shevchyk
quelle
updateEin vorübergehendes Objekt ist in Ordnung, ich habe keine Ausnahme erhalten.
GMsoF
Was ich weiß, wir können einen Übergang in keiner Weise aufrechterhalten. Ich denke, der Unterschied könnte zwischen distanziert und hartnäckig sein. Bitte korrigieren Sie mich.
Ram
Hier gibt es viele Fehler ... zB 1) ´save () ´ gibt kein "angehängtes Objekt" zurück, sondern die ´id´; 2) ´persist () ´ kann nicht garantiert werden, dass die ´id´ gesetzt wird, und es bleibt auch nicht "Objekt für DB bestehen"; ...
Eugen Labun
67
  • Im Hibernate-Forum finden Sie eine Erklärung der subtilen Unterschiede zwischen Persist und Save. Es sieht so aus, als ob der Unterschied die Zeit ist, zu der die INSERT-Anweisung letztendlich ausgeführt wird. Da save den Bezeichner zurückgibt, muss die INSERT-Anweisung unabhängig vom Status der Transaktion sofort ausgeführt werden (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. Speichern / Behalten arbeiten beide mit vorübergehenden Instanzen , dh Instanzen, denen noch keine Kennung zugewiesen wurde und die als solche nicht in der Datenbank gespeichert werden.

  • Update und Merge funktionieren beide für getrennte Instanzen , dh Instanzen, die einen entsprechenden Eintrag in der Datenbank haben, aber derzeit nicht an eine Sitzung angehängt (oder von dieser verwaltet) sind. Der Unterschied zwischen ihnen besteht darin, was mit der Instanz passiert, die an die Funktion übergeben wird. update versucht, die Instanz erneut zuzuordnen. Dies bedeutet, dass derzeit keine andere Instanz der persistenten Entität an die Sitzung angehängt sein darf. Andernfalls wird eine Ausnahme ausgelöst. Beim Zusammenführen werden jedoch nur alle Werte in eine persistente Instanz in der Sitzung kopiert (die geladen wird, wenn sie derzeit nicht geladen ist). Das Eingabeobjekt wird nicht geändert. Das Zusammenführen ist also allgemeiner als das Aktualisieren, kann aber mehr Ressourcen verbrauchen.

jrudolph
quelle
Einfügeanweisung
Im Falle eines getrennten Objekts löst das Zusammenführen eine Auswahl aus, während das Aktualisieren dies nicht tut.
Gab
1
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.Können Sie mir bitte sagen, wie eine Einfügung außerhalb einer Sitzung erfolgen kann und warum sie schlecht ist?
Erran Morad
Haftungsausschluss: Ich habe den Ruhezustand schon lange nicht mehr verwendet. IMO ist das Problem folgendes: Die Signatur und der Vertrag von save () erfordern, dass save eine Kennung für das neue Objekt zurückgibt. Abhängig von der von Ihnen gewählten ID-Generierungsstrategie wird der Bezeichner von der Datenbank generiert, wenn ein Wert INSERTbearbeitet wird. Folglich können Sie in diesen Fällen einen Bezeichner jetzt nicht zurückgeben, ohne ihn generiert zu haben, und um ihn zu generieren, müssen Sie ihn INSERT sofort ausführen . Da eine Transaktion mit langer Laufzeit derzeit nicht ausgeführt wird, sondern nur beim Festschreiben, besteht die einzige Möglichkeit, die Transaktion jetzt auszuführen INSERT, darin, sie außerhalb des Sendevorgangs auszuführen .
Jrudolph
12

Dieser Link erklärt auf gute Weise:

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

Wir alle haben diese Probleme, auf die wir nur selten genug stoßen, dass wir, wenn wir sie wiedersehen, wissen, dass wir sie gelöst haben, uns aber nicht erinnern können, wie.

Die NonUniqueObjectException, die bei Verwendung von Session.saveOrUpdate () im Ruhezustand ausgelöst wird, ist eine von mir. Ich werde einer komplexen Anwendung neue Funktionen hinzufügen. Alle meine Unit-Tests funktionieren einwandfrei. Beim Testen der Benutzeroberfläche und beim Versuch, 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 with 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 einfach update ()) für ein getrenntes Objekt aufrufen.

Wenn wir eine einzelne Ruhezustandssitzung schließen, werden die persistenten 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 Verfolgung 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 anbringen. Während dieses erneuten Anbringens überprüft Hibernate, ob weitere Kopien desselben Objekts vorhanden sind. Wenn es welche findet, muss es uns sagen, 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, aber Hibernate weiß nichts über sie, da sie zu diesem Zeitpunkt nicht verwaltet wurden.

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

Was sollen wir also tun? In Hibernate 3 haben wir merge () (in Hibernate 2 verwenden Sie saveOrUpdateCopy ()). Diese Methode zwingt den Ruhezustand, alle Änderungen von anderen getrennten Instanzen auf die zu speichernde Instanz zu kopieren, 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();

Es ist wichtig zu beachten, dass beim Zusammenführen ein Verweis auf die neu aktualisierte Version der Instanz zurückgegeben wird. Es wird kein Element erneut an die Sitzung angehängt. Wenn Sie beispielsweise die 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 Persistence API (JPA) kein Konzept für getrennte und erneut angehängte Objekte hat und EntityManager.persist () und EntityManager.merge () verwendet.

Ich habe im Allgemeinen festgestellt, dass saveOrUpdate () bei Verwendung von Hibernate normalerweise für meine Anforderungen ausreicht. Normalerweise muss ich Merge nur verwenden, wenn ich Objekte habe, die Verweise auf Objekte desselben Typs haben können. Zuletzt lag die Ursache der Ausnahme im Code, der bestätigte, dass die Referenz nicht rekursiv war. Ich habe dasselbe Objekt im Rahmen der Validierung in meine Sitzung geladen und den Fehler verursacht.

Wo sind Sie auf dieses Problem gestoßen? Hat Merge für Sie funktioniert oder brauchten Sie eine andere Lösung? Verwenden Sie lieber immer die Zusammenführung oder bevorzugen Sie es nur, wenn dies für bestimmte Fälle erforderlich ist

HakunaMatata
quelle
Link zum Artikel über Webarchive, da das Original nicht verfügbar ist: web.archive.org/web/20160521091122/http://www.stevideter.com:80/…
Eugen Labun
5

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

Wenn unsere Generatorklasse zugewiesen ist, gibt es keinen Unterschied zwischen save()und persist(Methoden. Da Generator 'zugewiesen' bedeutet, müssen wir als Programmierer den Primärschlüsselwert angeben, der in der Datenbank gespeichert werden soll. [Hoffe, Sie kennen dieses Generatorkonzept] Nehmen Sie bei einer anderen als der zugewiesenen Generatorklasse an, wenn unser Generatorklassenname Inkrement bedeutet Der Ruhezustand selbst weist den Primärschlüssel-ID-Wert in das Datenbankrecht ein [außer dem zugewiesenen Generator wird der Ruhezustand nur verwendet, um den Primärschlüssel-ID-Wert zu berücksichtigen]. In diesem Fall wird der Datensatz in den Fall save()oder die persist()Methode eingefügt Die Datenbank ist normal. Aber hören Sie, die save()Methode kann den Primärschlüssel-ID-Wert zurückgeben, der im Ruhezustand generiert wird, und wir können ihn sehen

long s = session.save(k);

In diesem Fall persist()wird dem Client niemals ein Wert zurückgegeben.

Hari Krishna
quelle
5

Ich habe ein gutes Beispiel gefunden, das die Unterschiede zwischen allen Speichermethoden im Ruhezustand zeigt:

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

Kurz gesagt, gemäß dem obigen Link:

sparen()

  • 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 Festschreibens der Transaktion oder beim Leeren der Sitzung gespeichert.

fortdauern()

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

saveOrUpdate ()

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

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

aktualisieren()

  • Die Aktualisierung im Ruhezustand sollte verwendet werden, wenn wir wissen, dass wir nur die Entitätsinformationen aktualisieren. Diese Operation fügt das Entitätsobjekt dem dauerhaften Kontext hinzu und weitere Änderungen werden verfolgt und gespeichert, wenn die Transaktion festgeschrieben wird.
  • Wenn wir also nach dem Aufruf von update Werte in der Entität festlegen, werden diese beim Festschreiben der Transaktion aktualisiert.

verschmelzen()

  • Durch Zusammenführen im Ruhezustand können vorhandene Werte aktualisiert werden. Diese Methode erstellt jedoch eine Kopie aus dem übergebenen Entitätsobjekt und gibt sie zurück. Das zurückgegebene Objekt ist Teil des persistenten Kontexts und wird auf Änderungen verfolgt. Das übergebene Objekt wird nicht verfolgt. Dies ist der Hauptunterschied zwischen merge () und allen anderen Methoden.

Praktische Beispiele für all dies finden Sie unter dem oben erwähnten Link. Es zeigt Beispiele für all diese verschiedenen Methoden.

Außer Sinnen
quelle
3

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

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

  • Vorübergehend (neu)
  • Verwaltet (dauerhaft)
  • Freistehend
  • Entfernt (gelöscht)

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

Beispielsweise stellt der JPA EntityManagerdie folgenden Entitätsstatusübergangsmethoden bereit.

Geben Sie hier die Bildbeschreibung ein

Die Ruhe Sessionimplementiert alle JPA EntityManagerMethoden und bietet einige zusätzliche Entität Zustandsübergangsverfahren wie save, saveOrUpdateund update.

Geben Sie hier die Bildbeschreibung ein

Fortdauern

Um den Status einer Entität von Transient (Neu) in Verwaltet (Persistent) zu ändern, können Sie die persistvon der JPA angebotene Methode verwenden, die auch vom Ruhezustand EntityManagergeerbt wird Session.

Die persistMethode löst eine aus, PersistEventdie vom DefaultPersistEventListenerHibernate-Ereignis-Listener 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()
    );
});

Der Ruhezustand 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 das idzugewiesen wird, bevor die BookEntität an den aktuellen Persistenzkontext angehängt wird. Dies ist erforderlich, da die verwalteten Entitäten in einer MapStruktur gespeichert sind, in der der Schlüssel aus dem Entitätstyp und seiner Kennung besteht und der Wert die Entitätsreferenz ist. Dies ist der Grund, warum die JPA EntityManagerund der WinterschlafSession als First-Level-Cache bezeichnet werden.

Beim Aufruf persistwird die Entität nur an den aktuell ausgeführten Persistenzkontext angehängt, und das INSERT kann bis zum verschoben werdenflush aufgerufen wird.

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

sparen

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

Die saveMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die saveMethode den Methoden updateund saveOrUpdate.

saveBetrachten Sie den folgenden Testfall, um zu sehen, wie die Methode 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
    );
});

Wenn Sie den obigen Testfall ausführen, 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 können, ist das Ergebnis identisch mit dem persistMethodenaufruf. Im Gegensatz dazu persistgibt die saveMethode jedoch die Entitätskennung zurück.

Weitere Informationen finden Sie in diesem Artikel .

Aktualisieren

Die Hibernate-spezifische updateMethode soll den Dirty-Checking-Mechanismus umgehen und eine Entitätsaktualisierung zum Flush-Zeitpunkt erzwingen.

Die updateMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die updateMethode den Methoden saveund saveOrUpdate.

Um zu sehen, wie die updateMethode funktioniert, betrachten Sie das folgende Beispiel, in dem eine BookEntitä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 updateMethodenaufrufs 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");
});

Bei der Ausführung 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 das UPDATEwährend des Persistence Context Flushs unmittelbar vor dem Festschreiben ausgeführt wird. Deshalb wird die Updating the Book entityNachricht zuerst protokolliert.

Verwenden @SelectBeforeUpdate, um unnötige Updates zu vermeiden

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

Wenn wir also die BookEntität mit der @SelectBeforeUpdateAnnotation 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 keine UPDATEAusführung erfolgt, da der Mechanismus für die Überprüfung des Ruhezustands im Ruhezustand festgestellt hat, dass die Entität nicht geändert wurde.

SaveOrUpdate

Die Hibernate-spezifische saveOrUpdateMethode ist nur ein Alias ​​für saveund update.

Die saveOrUpdateMethode löst eine aus, SaveOrUpdateEventdie vom DefaultSaveOrUpdateEventListenerHibernate-Ereignis-Listener verarbeitet wird. Daher entspricht die updateMethode den Methoden saveund saveOrUpdate.

Jetzt können Sie verwenden, saveOrUpdatewenn Sie eine Entität beibehalten oder eine erzwingen möchten, UPDATEwie 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 dem NonUniqueObjectException

Ein Problem, das bei und auftreten savekann update, saveOrUpdatebesteht 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 die 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)

Verschmelzen

Um dies zu vermeiden NonUniqueObjectException, müssen Sie mergedie von der JPA angebotene EntityManagerund auch vom Ruhezustand geerbte Methode verwenden Session.

Wie in diesem Artikel erläutert , mergeruft der Benutzer einen neuen Entitätsschnappschuss aus der Datenbank ab, wenn im Persistenzkontext keine Entitätsreferenz gefunden wird, und kopiert den Status der getrennten Entität, die an die mergeMethode übergeben wurde.

Die mergeMethode löst eine aus, MergeEventdie vom DefaultMergeEventListenerHibernate-Ereignis-Listener verarbeitet wird.

Um zu sehen, wie die mergeMethode funktioniert, betrachten Sie das folgende Beispiel, in dem eine BookEntität in einer Transaktion beibehalten wird. Anschließend wird sie geändert, während sich die Entität im getrennten Zustand befindet, und die getrennte Entität mergein einem Persistenzkontext der Teilsequenz übergeben.

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 hat Hibernate die folgenden SQL-Anweisungen ausgeführt:

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 zurückgegebene Entitätsreferenz von der getrennten mergeunterscheidet, die wir an die mergeMethode übergeben haben.

Obwohl Sie mergebeim Kopieren des Status der getrennten Entität die Verwendung von JPA bevorzugen sollten , kann das Extra SELECTbeim Ausführen einer Stapelverarbeitungsaufgabe problematisch sein.

Aus diesem Grund sollten Sie die Verwendung bevorzugen, updatewenn Sie sicher sind, dass dem aktuell ausgeführten Persistenzkontext noch keine Entitätsreferenz 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- persistMethode verwenden. Das Kopieren des Status der getrennten Entität mergesollte bevorzugt werden. Die updateMethode ist nur für Stapelverarbeitungsaufgaben nützlich. Die saveund saveOrUpdatesind nur Aliase updateund Sie sollten sie wahrscheinlich überhaupt nicht verwenden.

Einige Entwickler rufen saveauch dann auf, wenn die Entität bereits verwaltet wird. Dies ist jedoch ein Fehler und löst ein redundantes Ereignis aus, da bei verwalteten Entitäten das UPDATE automatisch zum Zeitpunkt des Löschens des Persistenzkontexts behandelt wird.

Weitere Informationen finden Sie in diesem Artikel .

Vlad Mihalcea
quelle
2

Beachten Sie, dass beim Aufrufen eines Updates für ein getrenntes Objekt immer ein Update in der Datenbank durchgeführt wird, unabhängig davon, ob Sie das Objekt geändert haben oder nicht. Wenn es nicht Ihren Wünschen entspricht, 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).

bernardn
quelle
1

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

Anton Popovich
quelle
11
Bitte teilen Sie uns mit, warum die folgenden Antworten nicht richtig sind.
Erran Morad
0

Keine der obigen Antworten ist vollständig. Obwohl Leo Theobald Antwort am nächsten Antwort aussieht.

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 auch in Bezug auf Flushes und Commits gesehen werden, was jeder völlig ignoriert zu haben scheint.

Verwenden Sie niemals die Speichermethode von HIBERNATE. VERGESSEN SIE, DASS ES AUCH IM HIBERNATE BESTEHT!

Fortdauern

Wie alle erklärten, wechselt Persist im Grunde genommen eine Entität 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 nicht mit Flush.

Wenn Sie zu diesem Zeitpunkt erneut "bestehen", ändert sich nichts. Und es wird keine weiteren Ersparnisse geben, wenn wir versuchen, eine beständige Entität beizubehalten.

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

Eine Räumung ist eine spezielle Funktion von Hibernate, die die Entität von "Verwaltet" in "Freistehend" überführt. 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 unterschiedliche Aufgaben ausführen, 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 "Losgelöst" eine Art "Offline" -Zustand bedeutet. und verwaltet bedeutet "Online" -Status.

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, sind Sie richtig. Dies löst eine Ausnahme aus, da die Zusammenführung für das Entitätsobjekt funktioniert hat, dessen Status getrennt ist. Aber es ändert nichts am Zustand des Objekts.

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, weil durch das Zusammenführen eine neue Entität in den Kontext gebracht wurde, der sich im dauerhaften Zustand befindet.

Bei Anwendung mit Update funktioniert dasselbe einwandfrei, da Update keine Kopie einer Entität wie Merge bringt.

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 keine SQL-Abfrage von select like merge ausgelöst hat.

löschen

Im obigen Beispiel habe ich delete verwendet, ohne über delete zu sprechen. Durch Löschen wird die Entität grundsätzlich vom verwalteten Status in den Status "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.

Ingwer Bier
quelle