Hazırda Bekletme'deki farklı kaydetme yöntemleri arasındaki farklar nelerdir?


199

Hazırda Bekletme, bir şekilde, nesnenizi alıp veritabanına koyan birkaç yöntem içerir. Aralarındaki farklar nelerdir, ne zaman kullanılır ve neden neyi ne zaman kullanacağını bilen tek bir akıllı yöntem yoktur?

Şimdiye kadar belirlediğim yöntemler şunlardır:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()

Yanıtlar:


117

İşte yöntemleri anladım. Temelde bunlar uygulamada tüm bunları kullanmıyorum olsa da API dayanmaktadır .

saveOrUpdate Bazı denetimlere bağlı olarak kaydeder veya günceller. Tanımlayıcı yoksa, kaydetme denir. Aksi takdirde güncelleme denir.

save Bir varlığı kalıcı hale getirir. Varsa bir tanımlayıcı atar. Biri yaparsa, aslında bir güncelleme yapıyor. Varlığın oluşturulan kimliğini döndürür.

update Varolan bir tanımlayıcıyı kullanarak varlığı devam ettirmeye çalışır. Tanımlayıcı yoksa, bir istisna atıldığını düşünüyorum.

saveOrUpdateCopy Bu kullanımdan kaldırılmıştır ve artık kullanılmamalıdır. Bunun yerine ...

birleştirme bilgim falter başlar budur Şimdi. Burada önemli olan, geçici, müstakil ve kalıcı varlıklar arasındaki farktır. Nesne durumları hakkında daha fazla bilgi için buraya bakın . Kaydet ve güncelle, kalıcı nesnelerle uğraşıyorsunuz. Bir Oturumla bağlantılıdırlar, böylece Hazırda Bekleme neyin değiştiğini bilir. Ancak geçici bir nesneniz olduğunda, herhangi bir oturum yoktur. Bu durumlarda, güncellemeler için birleştirme kullanmanız ve kaydetmeye devam etmeniz gerekir.

Kalıcı Yukarıda belirtildiği gibi, bu geçici nesneler üzerinde kullanılır. Oluşturulan kimliği döndürmez.


22
Bunu cevap olarak kabul etmek istiyorum, ancak bir şey hala belirsiz: save (), update () 'e geri döndüğünden, bu tür bir öğe varsa, pratikte saveOrUpdate ()' den nasıl farklıdır?
Henrik Paul

Nerede belirtilirse, bu kayıt ayrılmış örneklerde işe yarar mı?
jrudolph

2
Birleştirme / kalıcı tanımınız yalnızca geçici nesneler için önemliyse, bu bir ton anlam ifade eder ve hazırda bekletme yöntemimizi kullanır. Ayrıca, birleştirme işleminin bir tür bütünlük denetimi için fazladan getirme yaptığı için bir güncellemeyle karşılaştırıldığında genellikle performans sınırlamaları olduğunu unutmayın.
Martin Dale Lyness

1
Aşağıdaki jrudolph'un cevabı daha doğru.
azerole

2
Hibernate muhtemelen bir nesnenin hangi durumda olduğunu bilir, programı yazarken bunu neden manuel olarak yapmak zorundayız. Sadece bir kaydetme yöntemi olmalıdır.
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()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝

updategeçici bir nesne iyi, bir istisna alamadım.
GMsoF

Bildiğim kadarıyla, her iki şekilde de geçici olmaya devam edemeyiz. Bence farklılık müstakil ve inatçı olabilir. Lütfen düzelt beni.
Ram

burada birçok hata var ... örneğin 1) ´save () ´ "ekli nesne" döndürmez, ´id´ döndürür; 2) ´persist () ´ öğesinin ´id´ değerini ayarlaması garanti edilmez, hiçbiri "nesneyi DB olarak sürdürmez"; ...
Eugen Labun

67
  • Kalıcılık ve kaydetme arasındaki küçük farkların açıklaması için Hazırda Bekletme Forumu'na bakın . Fark, INSERT ifadesinin sonuçta yürütüldüğü zamandır. Yana kaydet tanımlayıcı dönmek yapar, INSERT deyimi anında bakılmaksızın (genellikle kötü bir şeydir) İşlemin devletin yürütülecek vardır. Persist , yalnızca tanımlayıcıyı atamak için şu anda çalışan işlemin dışında hiçbir ifade yürütmez. Kaydet / Devam Et, hem geçici örnekler üzerinde çalışır , yani henüz tanımlayıcı atanmamış örnekler ve DB'ye kaydedilmez.

  • Güncelle ve Birleştir her ikisi de ayrılmış örneklerde , yani DB'de karşılık gelen bir girişi olan ancak şu anda bir Oturuma bağlı olmayan (veya bir Oturum tarafından yönetilmeyen) örneklerde çalışır . Aralarındaki fark, işleve iletilen örneğe olan şeydir. güncelleştirme örneği yeniden bağlamaya çalışır; bu, şu anda Oturum'a bağlı kalıcı varlığın başka bir örneğinin olmaması gerektiği anlamına gelir, aksi takdirde bir istisna atılır. ancak birleştirme , tüm değerleri Oturumda kalıcı bir örneğe kopyalar (o anda yüklü değilse yüklenir). Giriş nesnesi değiştirilmez. Bu nedenle birleştirme daha geneldir güncellemeden, ancak daha fazla kaynak kullanabilir.


kendi Id üreteciniz varsa açıklama ekleme hala gerçekleşmiyor
kommradHomer

Ayrılmış nesne durumunda, birleştirme bir seçimi tetikler, oysa güncelleme olmaz mı?
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.Bir ekin oturum dışında nasıl olabileceğini ve neden kötü olduğunu söyleyebilir misiniz?
Erran Morad

Feragatname: Uzun süredir hazırda bekletme modunu kullanmadım. IMO sorunu şudur: save () imzası ve sözleşmesi, save'in yeni nesne için bir tanımlayıcı döndürmesini gerektirir. Seçtiğiniz kimlik oluşturma stratejisine bağlı olarak, bir değer düzenlendiğinde tanımlayıcı DB tarafından oluşturulur INSERT. Sonuç olarak, bu durumlarda bunu oluşturulan kalmadan hemen bir tanımlayıcı döndüremez ve çalıştırmak zorunda bunu üretmek için INSERT şu an . Uzun süredir devam eden bir işlem şu anda değil sadece taahhütte çalıştırıldığı için, INSERTşimdi yürütmenin tek yolu bu işlemi tx dışında çalıştırmaktır.
jrudolph

12

Bu bağlantı iyi bir şekilde açıklıyor:

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

Hepimiz yeterince seyrek karşılaştığımız sorunlar var, onları tekrar gördüğümüzde bunu çözdüğümüzü biliyoruz, ama nasıl olduğunu hatırlayamıyoruz.

Hazırda Bekletme'de Session.saveOrUpdate () kullanılırken atılan NonUniqueObjectException benimkilerden biridir. Karmaşık bir uygulamaya yeni işlevler ekleyeceğim. Tüm birim testlerim iyi çalışıyor. Daha sonra, bir nesneyi kaydetmeye çalışırken, kullanıcı arayüzünü test ederken, "oturumla aynı tanımlayıcı değere sahip farklı bir nesne zaten ilişkilendirildi" mesajıyla bir istisna almaya başladım. İşte Hazırda Bekletme ile Java Kalıcılık bazı örnek kod.

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

Bu özel durumun nedenini anlamak için, ayrılmış nesneleri ve ayrılmış bir nesnede saveOrUpdate () (veya yalnızca update ()) öğesini çağırdığınızda ne olduğunu anlamak önemlidir.

Tek bir Hazırda Bekletme Oturumunu kapattığımızda, birlikte çalıştığımız kalıcı nesneler ayrılır. Bu, verilerin hala uygulamanın belleğinde olduğu anlamına gelir, ancak Hazırda Beklet artık nesnelerde yapılan değişiklikleri izlemekle sorumlu değildir.

Daha sonra müstakil neslimizi değiştirir ve güncellemek istersek, nesneyi yeniden bağlamamız gerekir. Bu yeniden bağlama işlemi sırasında, Hazırda Beklet aynı nesnenin başka kopyaları olup olmadığını kontrol eder. Herhangi bir şey bulursa, bize “gerçek” kopyanın artık ne olduğunu bilmediğini söylemek zorundadır. Belki de kurtarılmasını beklediğimiz diğer kopyalarda başka değişiklikler yapıldı, ancak Hibernate bunları bilmiyor, çünkü o zaman onları yönetmiyordu.

Hazırda Bekleme, muhtemelen hatalı verileri kaydetmek yerine, bize NonUniqueObjectException aracılığıyla sorunu anlatır.

Peki ne yapacağız? Hazırda Bekletme 3'te merge () yöntemimiz vardır (Hazırda Bekletme 2'de saveOrUpdateCopy () kullanın). Bu yöntem, Hazırda Beklet'i diğer ayrılmış örneklerde yapılan değişiklikleri kaydetmek istediğiniz örneğe kopyalamaya zorlar ve böylece kaydetmeden önce bellekteki tüm değişiklikleri birleştirir.

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

Birleştirme işleminin örneğin yeni güncellenen sürümüne bir başvuru döndürdüğünü belirtmek önemlidir. Öğeyi Oturum'a yeniden eklemek değil. Örneğin eşitliğini test ederseniz (item == item3), bu durumda bunun false değerini döndürdüğünü görürsünüz. Muhtemelen bu noktadan itibaren item3 ile çalışmak isteyeceksiniz.

Java Kalıcılık API'sının (JPA) ayrılmış ve yeniden bağlanmış nesneler kavramına sahip olmadığını ve EntityManager.persist () ve EntityManager.merge () yöntemlerini kullandığını da not etmek önemlidir.

Genel olarak, Hazırda Bekletme'yi kullanırken, saveOrUpdate () yönteminin genellikle ihtiyaçlarım için yeterli olduğunu fark ettim. Genellikle aynı türdeki nesnelere başvuruları olan nesneler olduğunda birleştirme kullanmam gerekir. En son olarak, kural dışı durumun nedeni, başvurunun özyinelemediğini doğrulayan kodda idi. Hataya neden olan doğrulama işleminin bir parçası olarak oturumuma aynı nesneyi yüklüyordum.

Bu problemle nerede karşılaştınız? Birleştirme sizin için mi işe yaradı yoksa başka bir çözüme mi ihtiyacınız var? Her zaman birleştirme kullanmayı mı, yoksa yalnızca belirli durumlar için gerektiği gibi kullanmayı mı tercih edersiniz?


Orijinalin bulunmadığı için webarchive'daki makaleye bağlantı
Eugen Labun

5

Hazırda bekletme save()ve persist()yöntemler arasındaki fark , kullandığımız jeneratör sınıfına bağlıdır.

Jeneratör sınıfımız atanmışsa, save()ve persist() yöntemleri arasında fark yoktur . Jeneratör 'atanmış' anlamına gelir, bir programcı olarak veritabanına kaydetmek için birincil anahtar değerini vermeliyiz [Umarım bu jeneratörler konseptini bilirsiniz] Atanmış jeneratör sınıfı dışında bir durumda, jeneratör sınıfı adımızın Artış anlamı olup olmadığını varsayalım Hazırda bekletme modu, birincil anahtar kimlik değerini veritabanına doğru atayacaktır [atanmış jeneratör dışında, hazırda bekletme yalnızca birincil anahtar kimliği değerini hatırlamak için kullanılır], bu durumda bu durumda arama yaparsak save()veya persist()yöntem kullanırsak, kayıt Ama normalde duymak, save()yöntem hazırda bekletme tarafından oluşturulan birincil anahtar id değerini döndürebilir ve biz görebiliyoruz

long s = session.save(k);

Aynı durumda, persist()asla istemciye herhangi bir değer vermeyecektir.


5

Tüm hazırda bekleme kaydetme yöntemleri arasındaki farkları gösteren iyi bir örnek buldum:

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

Kısaca, yukarıdaki bağlantıya göre:

kayıt etmek()

  • Bu yöntemi bir işlemin dışında çağırabiliriz. Bunu işlem yapmadan kullanırsak ve varlıklar arasında basamaklandırırsak, oturumu temizlemedikçe yalnızca birincil varlık kaydedilir.
  • Bu nedenle, birincil nesneden eşlenen başka nesneler varsa, işlem yapılırken veya oturumu temizlediğimizde kaydedilirler.

) (Devam

  • Bu işlemde save () kullanmaya benzer, bu yüzden güvenlidir ve basamaklı nesnelerle ilgilenir.

SaveOrUpdate ()

  • İşlemle birlikte veya işlem olmadan kullanılabilir ve save () gibi, işlem olmadan kullanılırsa, eşlenen varlıklar kaydedilmeyecektir; es, oturumu temizleriz.

  • Sağlanan verilere göre sorgu ekleme veya güncelleme sorguları. Veri veritabanında varsa, güncelleme sorgusu yürütülür.

Güncelleme()

  • Hazırda bekletme güncelleştirmesi, yalnızca varlık bilgilerini güncellediğimizi bildiğimiz yerlerde kullanılmalıdır. Bu işlem varlık nesnesini kalıcı bağlama ekler ve işlem yapıldığında diğer değişiklikler izlenir ve kaydedilir.
  • Bu nedenle, güncelleme çağrıldıktan sonra bile, varlıkta herhangi bir değer ayarlarsak, işlem tamamlandığında bunlar güncellenir.

birleştirmek()

  • Hazırda bekletme, varolan değerleri güncellemek için kullanılabilir, ancak bu yöntem iletilen varlık nesnesinden bir kopya oluşturur ve döndürür. Döndürülen nesne kalıcı bağlamın bir parçasıdır ve herhangi bir değişiklik için izlenir, iletilen nesne izlenmez. Bu, merge () ile diğer tüm yöntemlerin en büyük farkıdır.

Ayrıca tüm bunların pratik örnekleri için lütfen yukarıda bahsettiğim bağlantıya bakın, tüm bu farklı yöntemlerin örneklerini gösterir.


3

Bu makalede açıkladığım gibi , çoğu zaman JPA yöntemlerini veupdate toplu işlem görevleri için .

Bir JPA veya Hazırda Bekletme varlığı aşağıdaki dört durumdan birinde olabilir:

  • Geçici (Yeni)
  • Yönetilen (Kalıcı)
  • bağımsız
  • Kaldırıldı (Silindi)

Bir durumdan diğerine geçiş EntityManager veya Session yöntemleri aracılığıyla yapılır.

Örneğin, JPA EntityManageraşağıdaki varlık durumu geçiş yöntemlerini sağlar.

resim açıklamasını buraya girin

Hazırda Beklet, Sessiontüm JPA EntityManageryöntemlerini uygular ve save, saveOrUpdateve gibi bazı ek varlık durumu geçiş yöntemleri sağlar update.

resim açıklamasını buraya girin

Persist

Bir varlığın durumunu Geçici (Yeni) yerine Yönetilen (Kalıcı) olarak değiştirmek için, Hazırda Bekletme tarafından da devralınan persistJPA tarafından sunulan yöntemi kullanabiliriz .EntityManagerSession

persistYöntem, bir tetikler PersistEventile kullanılan ve DefaultPersistEventListenerhazırda olay dinleyicisi.

Bu nedenle, aşağıdaki test senaryosunu yürütürken:

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()
    );
});

Hazırda Beklet aşağıdaki SQL deyimlerini oluşturur:

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
)

Varlığın mevcut Kalıcı Bağlam'a ideklenmeden önce atandığına dikkat edin Book. Yönetilen varlıklar Map, anahtarın varlık türü ve tanımlayıcısı tarafından oluşturulduğu ve değerin varlık başvurusu olduğu bir yapıda depolanması nedeniyle bu gereklidir . JPA EntityManagerve Hazırda Bekletme'nin SessionBirinci Düzey Önbellek olarak bilinmesinin nedeni budur .

Arama yaparken persist, varlık yalnızca şu anda çalışmakta olan Kalıcılık Bağlamına eklenir ve INSERT,flush .

Tek istisna, INSERT'i hemen tetikleyen IDENTITY oluşturucusudur, çünkü varlık tanımlayıcısını almanın tek yolu budur. Bu nedenle, Hazırda Bekletme, IDENTITY üretecini kullanan varlıklar için ekleri toplu işleyemez. Bu konu hakkında daha fazla bilgi için bu makaleye göz atın .

Kayıt etmek

Hazırda Bekleme özgü saveyöntem JPA'dan önce gelir ve Hazırda Bekletme projesinin başından beri kullanılabilir.

saveYöntem, bir tetikler SaveOrUpdateEventile kullanılan ve DefaultSaveOrUpdateEventListenerhazırda olay dinleyicisi. Bu nedenle, saveyöntem updateve saveOrUpdateyöntemlerine eşdeğerdir .

saveYöntemin nasıl çalıştığını görmek için aşağıdaki test durumunu göz önünde bulundurun:

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

Yukarıdaki test senaryosunu çalıştırırken, Hazırda Beklet aşağıdaki SQL deyimlerini oluşturur:

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
)

Gördüğünüz gibi, sonuç persistyöntem çağrısıyla aynıdır . Ancak, aksine persist, saveyöntem varlık tanımlayıcı döndürür.

Daha fazla ayrıntı için bu makaleye göz atın .

Güncelleme

Hazırda Bekletme özelliğine yönelik updateyöntem, kirli denetim mekanizmasını atlamak ve bir varlık sifonunu güncellemeye zorlamak içindir.

updateYöntem, bir tetikler SaveOrUpdateEventile kullanılan ve DefaultSaveOrUpdateEventListenerhazırda olay dinleyicisi. Bu nedenle, updateyöntem saveve saveOrUpdateyöntemlerine eşdeğerdir .

updateYöntemin nasıl çalıştığını görmek için Book, bir işlemde bir varlığa devam eden aşağıdaki örneği göz önünde bulundurun , sonra varlık ayrık durumdayken onu değiştirir ve updateyöntem çağrısını kullanarak SQL GÜNCELLEMESİ'ni zorlar .

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

Yukarıdaki test senaryosunu yürütürken, Hazırda Beklet aşağıdaki SQL deyimlerini oluşturur:

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

UPDATEDevamlılık Bağlamı sırasında, kararlılıktan hemen önce yürütüldüğüne dikkat edin ve bu yüzden önce Updating the Book entitymesaj günlüğe kaydedilir.

@SelectBeforeUpdateGereksiz güncellemeleri önlemek için kullanma

Şimdi, varlık ayrık durumdayken değiştirilmese bile UPDATE her zaman yürütülecektir. Bunu önlemek için , getirilen @SelectBeforeUpdatebir SELECTifadeyi tetikleyecek Hazırda Bekletme notunu kullanabilirsiniz.loaded state ve daha sonra kirli kontrol mekanizması tarafından kullanılan .

Dolayısıyla, Bookvarlığı @SelectBeforeUpdateek açıklama ile eklersek:

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

    //Code omitted for brevity
}

Ve aşağıdaki test senaryosunu yürütün:

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

Hazırda Bekletme aşağıdaki SQL deyimlerini yürütür:

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

Bu kez, UPDATEHazırda Bekletme kirli denetim mekanizması varlığın değiştirilmediğini algıladığından, yürütülmediğine dikkat edin.

SaveOrUpdate

Hazırda özgü saveOrUpdateyöntem sadece için takma saveve update.

saveOrUpdateYöntem, bir tetikler SaveOrUpdateEventile kullanılan ve DefaultSaveOrUpdateEventListenerhazırda olay dinleyicisi. Bu nedenle, updateyöntem saveve saveOrUpdateyöntemlerine eşdeğerdir .

Şimdi, saveOrUpdatebir varlığı devam ettirmek veya UPDATEaşağıdaki örnekte gösterildiği gibi a'yı zorlamak için kullanabilirsiniz .

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

Dikkat edin NonUniqueObjectException

İle oluşabilir bir problem save, updateve saveOrUpdateSebat Bağlam zaten aynı kimliğe sahip ve aşağıdaki örnekte olduğu gibi aynı tipte bir varlık başvuru içeriyorsa geçerli:

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

Şimdi, yukarıdaki test senaryosunu yürütürken, Hazırda Beklet öğesi bir atar NonUniqueObjectExceptionçünkü ikincisi EntityManagerzaten Bookgeçtiğimizle aynı tanımlayıcıya sahip bir varlık içerdiğinden updateve Kalıcılık Bağlamı aynı varlığın iki temsilini tutamaz.

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)

Birleştirmek

Bundan kaçınmak için NonUniqueObjectException, mergeJPA tarafından sunulan EntityManagerve Hazırda Beklet tarafından devralınan yöntemi kullanmanız gerekir Session.

Açıklandığı gibi bu makalede , mergehiçbir varlık referansı Sebat Bağlamında bulundu varsa veritabanından yeni taraf anlık bir getirmektedir ve müstakil varlığın kopyalar devlet geçirilen mergeyöntemle.

mergeYöntem, bir tetikler MergeEventile kullanılan ve DefaultMergeEventListenerhazırda olay dinleyicisi.

mergeYöntemin nasıl çalıştığını görmek için Book, bir işlemde bir varlığa devam eden aşağıdaki örneği göz önünde bulundurun , ardından varlık ayrık durumdayken onu değiştirir ve ayrılmış varlığı mergebir sonraki Kalıcılık Bağlamına geçirir.

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

Yukarıdaki test senaryosunu çalıştırırken, Hazırda Beklet aşağıdaki SQL deyimlerini yürüttü:

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

Tarafından döndürülen varlık referansının merge, mergeyönteme ilettiğimiz müstakil referanstan farklı olduğuna dikkat edin .

Şimdi, mergeayrılmış varlık durumunu kopyalarken JPA kullanmayı tercih etmenize rağmen , SELECTtoplu işleme görevini yürütürken fazlalık sorunlu olabilir.

Bu nedenle, updateo anda çalışan Kalıcılık Bağlamına eklenmiş bir varlık referansı olmadığından ve ayrılmış varlığın değiştirildiğinden emin olduğunuzda kullanmayı tercih etmelisiniz .

Bu konu hakkında daha fazla bilgi için bu makaleye göz atın .

Sonuç

Bir varlığı sürdürmek için JPA persistyöntemini kullanmanız gerekir . Müstakil varlık durumunu kopyalamak için mergetercih edilmelidir. updateYöntem toplu işlemler, sadece için yararlıdır. saveVe saveOrUpdatesadece rumuzlarıdırlar updateve muhtemelen tüm bunları kullanmamalısınız.

Bazı geliştiriciler save, varlık zaten yönetilse bile çağırır , ancak bu bir hatadır ve yönetilen varlıklar için UPDATE, kalıcılık bağlamında yıkama zamanında otomatik olarak işlenir.

Daha fazla ayrıntı için bu makaleye göz atın .


2

Ayrılmış bir nesnede güncelleme çağırırsanız, nesneyi değiştirseniz de değiştirmeseniz de her zaman veritabanında bir güncelleme yapılacağını unutmayın. İstediğiniz gibi değilse LockMode.None ile Session.lock () kullanmalısınız.

Güncellemeyi yalnızca nesne geçerli oturumunuzun kapsamı dışında değiştirildiyse çağırmalısınız (bağımsız moddayken).



0

Yukarıdaki cevapların hiçbiri tam değildir. Leo Theobald cevabı en yakın cevaba benzese de.

Temel nokta, hazırda bekletme durumunun varlık durumlarıyla nasıl başa çıktığı ve bir durum değişikliği olduğunda onları nasıl ele aldığıdır. Her şey, herkesin tamamen görmezden geldiği görünen kızarma ve taahhütlerle ilgili olarak görülmelidir.

HIBERNATE TASARRUFU KULLANMAYIN. HIBERNATE'DE OLDUĞUNU UNUTUN!

Persist

Herkesin açıkladığı gibi, Persist bir varlığı "Geçici" durumdan "Yönetilen" Devlete geçirir. Bu noktada, bir slush veya kesinleştirme ekleme ifadesi oluşturabilir. Ancak işletme halen "Yönetilen" durumda kalacaktır. Gömme ile değişmez.

Bu noktada, yine "Kalıcı" olursanız, değişiklik olmaz. Ve eğer varlığını sürdürebilmiş bir varlığa devam etmeye çalışırsak, artık daha fazla tasarruf olmayacak.

Eğlence, varlığı tahliye etmeye çalıştığımızda başlar.

Tahliye, Hazırda Bekletme özelliğinin "Yönetilen" durumundan "Ayrılmış" durumuna geçireceği özel bir işlevidir. Müstakil bir varlığa kalıcılık diyemeyiz. Bunu yaparsak, Hazırda Beklet bir istisna oluşturur ve işlemin tamamı kesinleştirme sırasında geri alınır.

Birleştirme ve Güncelleme

Bunlar, farklı şekillerde ele alındığında farklı şeyler yapan 2 ilginç işlevdir. Her ikisi de varlığı "Müstakil" durumdan "Yönetilen" duruma geçirmeye çalışıyor. Ama farklı yapıyoruz.

Ayrılmış'ın bir tür "çevrimdışı" durum anlamına geldiğini anlayın. ve yönetilen "Çevrimiçi" durum anlamına gelir.

Aşağıdaki kodu inceleyin:

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

Bunu ne zaman yaptın? Sizce ne olacak? Bunun istisna doğuracağını söylediyseniz, haklısınız demektir. Bu bir istisna doğuracaktır, çünkü birleştirme, devletin ayrılması olan varlık nesnesi üzerinde çalışmıştır. Fakat nesnenin durumunu değiştirmez.

Sahnenin arkasında, birleştirme bir seçme sorgusu oluşturur ve temelde ekli durumdaki varlığın bir kopyasını döndürür. Aşağıdaki kodu inceleyin:

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

Yukarıdaki örnek işe yarıyor çünkü birleştirme, kalıcı bir durumda olan bağlam içine yeni bir varlık getirdi.

Güncelleme ile uygulandığında, güncelleme aslında birleştirme gibi varlığın bir kopyasını getirmediği için aynı işe yarar.

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

Aynı zamanda hata ayıklama izlemesinde Update'in birleştirme gibi seçme SQL sorgusunu yükseltmediğini görebiliriz.

silmek

Yukarıdaki örnekte silme hakkında konuşmadan sil komutunu kullandım. Sil, temelde varlığı yönetilen durumdan "kaldırıldı" durumuna geçirir. Ve temizlendiğinde veya işlendiğinde saklamak için bir silme komutu gönderir.

Ancak, persist yöntemini kullanarak varlığı "kaldırılmış" durumdan "yönetilen" duruma geri getirmek mümkündür.

Umarım yukarıdaki açıklama herhangi bir şüpheyi açıklığa kavuşturur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.