JPA EntityManager: Neden merge () yerine persist () kullanılır?


Yanıtlar:


1615

Her iki şekilde de bir varlık PersistenceContext'e eklenir, fark, daha sonra varlıkla yaptığınız şeydir.

Persist, bir varlık örneğini alır, onu bağlama ekler ve bu örneği yönetilmesini sağlar (yani, varlığın gelecekteki güncellemeleri izlenir).

Birleştirme, durumun birleştirildiği yönetilen örneği döndürür. PersistenceContext içinde var olan bir şeyi döndürür veya varlığınızın yeni bir örneğini oluşturur. Her durumda, durumu sağlanan varlıktan kopyalar ve yönetilen kopyayı döndürür. Aktardığınız örnek yönetilmeyecek (birleştirme işlemini tekrar çağırmadıkça yaptığınız değişiklikler işlemin bir parçası olmayacaktır). Döndürülen örneği (yönetilen) kullanabilirsiniz.

Belki bir kod örneği yardımcı olacaktır.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Senaryo 1 ve 3 kabaca eşdeğerdir, ancak Senaryo 2'yi kullanmak isteyebileceğiniz bazı durumlar vardır.


3
@dma_k: Hibernate kullandığınız anlaşılıyor. Hibernate'e JPA'dan daha az aşinayım - ancak EntityManager.persist () öğesini çağırır ve müstakil bir varlıkta geçerseniz JPA'da: a) hemen bir EntityExistsException'ı alırsınız veya b) flush / kesinleştirme zamanında başka bir PersistenceException alabilirsiniz. Belki de buradaki soruyu yanlış anladım?
Mike

49
Bu yanıt, birleştirilen / devam ettirilen varlığın kalıcı bağlamda zaten var olduğu (veya en azından yalnızca kalıcı / birleştirilen varlığın zaten mevcut olmadığındaki davranışı açıkladığını açıklığa kavuşturduğu) durumları da kapsaması durumunda geliştirilebilir
Henry

2
Bir yöntem daha performanslı mı? Belki de mergebir nesnenin yönetilmeden önce tam kopyası bir performans isabetine sahiptir?
Kevin Meredith

2
Kimlikler ne olacak? Varsa @GeneratedIdsenaryo 2'de alabilir miyim?
rascio

7
Mike: "Birleştirme yeni bir örnek oluşturur ...": bu her zaman doğru değildir. EntityManager zaten yönetilen bir varlığı bağlamında bulursa bu örneği döndürür (alanları güncelledikten sonra). Lütfen cevabınızı düzenleyin, sonra buna oy vereceğim.
Heri

181

Kalıcılık ve birleşme iki farklı amaç içindir (hiç alternatif değildirler).

(farklılık bilgilerini genişletmek için düzenlenmiştir)

inat:

  • Veritabanına yeni bir kayıt ekleme
  • Nesneyi varlık yöneticisine ekleyin.

birleştirmek:

  • Aynı kimliğe sahip bir ekli nesne bulun ve güncelleyin.
  • Varsa, zaten eklenmiş nesneyi güncelleyin ve döndürün.
  • Eğer yoksa, yeni kaydı veritabanına ekleyin.

sürekli () verimlilik:

  • Bir veritabanına yeni bir kayıt eklemek için birleştirme () yönteminden daha etkili olabilir.
  • Orijinal nesneyi çoğaltmaz.

persist () anlambilimi:

  • Yanlışlıkla eklemenizi ve güncellememenizi sağlar.

Misal:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Bu şekilde varlık yöneticisindeki herhangi bir kayıt için yalnızca 1 ekli nesne bulunur.

kimliğine sahip bir varlık için merge () yöntemi şöyledir:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Her ne kadar MySQL birleştirme () ile bağlanmışsa ON DUPLICATE KEY UPDATE seçeneğiyle INSERT çağrısı kullanılarak persist () kadar etkili olabilse de, JPA çok üst düzey bir programdır ve bunun her yerde böyle olacağını varsayamazsınız.


Bunu yerine geçerli değil dava isim Can em.persist(x)ile x = em.merge(x)?
Aaron Digulla

20
persist () bir EntityExistsException durumu atabilir. Kodunuzun, verilerin bir güncellemesini değil, bir eklediğinden emin olmak istiyorsanız, kullanmanız gerekir.
Josep Panadero

1
merge()da atabilirEntityExistsException
Sean

1
@Hiçbiri olabilir çünkü bir RuntimeException, ama Javadoc'da belirtilmemiş.
Martin

154

Atanmış oluşturucuyu kullanıyorsanız, sürekli yerine birleştirme kullanmak gereksiz bir SQL deyimine neden olabilir , bu nedenle performansı etkileyebilir.

Ayrıca, yönetilen varlıklar için birleştirme çağrısında yönetilen varlıklar otomatik hazırda tarafından yönetilir ve bunların devlet tarafından veritabanı kaydı ile senkronize edildiğinden ayrıca bir hatadır kirli kontrol mekanizması üzerine Sebat Bağlamını kızarma .

Tüm bunların nasıl çalıştığını anlamak için önce Hazırda Beklet'in geliştirici zihniyetini SQL deyimlerinden varlık durumu geçişlerine kaydırdığını bilmelisiniz .

Bir varlık Hazırda Bekletme tarafından aktif olarak yönetildikten sonra, tüm değişiklikler otomatik olarak veritabanına yayılır.

Hazırda bekletme modu, halihazırda bağlı olan varlıkları izler. Ancak bir işletmenin yönetilebilmesi için doğru varlık durumunda olması gerekir.

JPA durum geçişlerini daha iyi anlamak için aşağıdaki diyagramı görselleştirebilirsiniz:

JPA varlık durumu geçişleri

Veya Hazırda Bekletme özel API'sini kullanıyorsanız:

Hazırda bekletme durumu geçişleri

Yukarıdaki şemalarda gösterildiği gibi, bir varlık aşağıdaki dört durumdan birinde olabilir:

  • Yeni (Geçici)

    Bir Hazırda Bekletme Session(akaPersistence Context ) ve herhangi bir veritabanı tablosu satırına eşlenmemiş Yeni (Geçici) durumunda olduğu kabul edilir.

    Kalıcı olmak için ya EntityManager#persistyöntemi açıkça çağırmamız ya da geçişli kalıcılık mekanizmasını kullanmamız gerekir.

  • Kalıcı (Yönetilen)

    Kalıcı bir varlık, bir veritabanı tablosu satırıyla ilişkilendirildi ve şu anda çalışan Persistence Context tarafından yönetiliyor. Böyle bir kuruluşta yapılan herhangi bir değişiklik tespit edilecek ve veritabanına yayılacaktır (Oturum boşaltma süresi boyunca). Hazırda Bekletme ile artık INSERT / UPDATE / DELETE deyimlerini yürütmemiz gerekmiyor. Hazırda Beklet, işlemsel bir yazma arkası çalışma stili kullanır ve değişiklikler en son sorumlu anda, geçerli Sessionyıkama zamanı sırasında senkronize edilir .

  • bağımsız

    Çalışmakta olan Persistence Context kapatıldıktan sonra, önceden yönetilen tüm varlıklar ayrılır. Art arda yapılan değişiklikler artık izlenmeyecek ve otomatik veritabanı senkronizasyonu gerçekleşmeyecek.

    Ayrılmış bir varlığı etkin bir Hazırda Bekletme Oturumuyla ilişkilendirmek için aşağıdaki seçeneklerden birini belirleyebilirsiniz:

    • yeniden takma

      Hazırda Beklet (ancak JPA 2.1 değil), Oturum # güncelleme yöntemiyle yeniden eklemeyi destekler. Hazırda Bekletme Oturumu, belirli bir veritabanı satırı için yalnızca bir Varlık nesnesini ilişkilendirebilir. Bunun nedeni Kalıcılık Bağlamının bir bellek içi önbellek (birinci düzey önbellek) görevi görmesi ve belirli bir anahtarla (varlık türü ve veritabanı tanımlayıcısı) yalnızca bir değerin (varlık) ilişkilendirilmesidir. Varlık yalnızca geçerli Hazırda Bekletme Oturumuyla ilişkilendirilmiş başka bir JVM nesnesi (aynı veritabanı satırıyla eşleşen) yoksa yeniden bağlanabilir.

    • birleştirme

    Birleştirme, ayrılmış varlık durumunu (kaynak) yönetilen varlık örneğine (hedef) kopyalayacaktır. Birleştirilen varlığın geçerli Oturumda eşdeğeri yoksa, biri veritabanından getirilir. Ayrılmış nesne örneği, birleştirme işleminden sonra bile ayrılmaya devam eder.

  • Kaldırılan

    JPA yalnızca yönetilen varlıkların kaldırılmasına izin verilmesine rağmen, Hazırda Beklet ayrıca ayrılan varlıkları da silebilir (ancak yalnızca bir Oturum # silme yöntemi çağrısı yoluyla). Kaldırılan varlık yalnızca silinmek üzere zamanlanır ve gerçek veritabanı DELETE deyimi Oturum çalma süresi sırasında yürütülür.



@gstackoverflow Aldığınız cevap doğru. Daha fazla ayrıntı için bu makaleye veya Yüksek Performanslı Java Kalıcılığı adlı kitabıma göz atın .
Vlad Mihalcea

Bu nedenle, yetimremoval = true için işlem sırasını değiştirme imkanı yoktur?
gstackoverflow

Her zamanki gibi operasyon sırası hakkındaki makaleniz. OrphanRemoval
gstackoverflow

1
Gerçek şu ki, kış uykusunu böyle bir diyagramla açıklamak imkansızdır. Neden ayrıldıktan sonra oturumu yıkamıyorsunuz? Zaten var olan bir varlığı kaydetmeye çalıştığınızda ne olur? Kaydetme ve devam etme söz konusu olduğunda, bu sifon davranışı neden farklıdır? Kimsenin açık bir mantığı olmayan 1000 soru var.
GingerBeer

37

Kullandığımda em.merge, JPA'nın benim için ürettiği bir alan olmasa bile, SELECTher biri için bir açıklama aldım INSERT- birincil anahtar alanının kendimi ayarladığım bir UUID olduğunu fark ettim. Geçtim em.persist(myEntityObject)ve sadece INSERTifadeler aldım .


3
Kimlikleri atadığınızdan ve JPA kapsayıcısının bunu nereden aldığınıza dair hiçbir fikri olmadığı için mantıklı. Nesnenin veritabanında zaten mevcut olması ihtimali düşüktür (örneğin, birkaç uygulamanın aynı veritabanına yazdığı bir senaryoda).
Aaron Digulla

Benzer bir sorunla karşılaştım merge(). Ben karmaşık görünümü ile PostgreSQL veritabanı vardı : görünümü birkaç tablodan veri toplandı (tablolar aynı yapıya sahipti ama farklı isimler). Böylece JPA yapmaya çalıştı merge(), ama aslında JPA ilk olarak yapıldı SELECT(görünüm ayarları nedeniyle veritabanı farklı tablolardan aynı birincil anahtarla birkaç kayıt döndürebilir!), Ardından JPA (Hazırda Beklet bir uygulama idi) başarısız oldu: aynı anahtarla birkaç kayıt var ( org.hibernate.HibernateException: More than one row with the given identifier was found). Benim durumumda persist()bana yardımcı oldu.
flaz14

29

JPA spesifikasyonu hakkında şunları söylüyor persist().

Eğer X, bir müstakil amacı, EntityExistsExceptionçalışma çağrılır devam veya atılır edilebilir EntityExistsExceptionveya başka bir PersistenceExceptiontemizleme atılan edilebilir ya da zaman işlemek.

Bu nedenle persist(), nesne ayrılmış bir nesne olmamalı olduğunda kullanmak uygun olacaktır . Kodun PersistenceExceptionhızlı bir şekilde başarısız olması için atmasını tercih edebilirsiniz .

Şartname belirsiz olsa da , bir nesne için persist()ayarlayabilir @GeneratedValue @Id. merge()ancak @Idönceden oluşturulmuş bir nesneye sahip olmalıdır .


5
+1 için " merge()Ancak, @Id önceden oluşturulmuş bir nesneye sahip olmalıdır . " EntityManager nesne kimliği alanı için bir değer bulamadığında, veri tabanına kalıcı olarak eklenir (eklenir).
Omar

Ben devletler üzerinde net olmadığı için bu ilk anlayamadım. Umarım bu benim için olduğu gibi birine yardımcı olur. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz

1
@GeneratedValue birleştirme () ve persist () için farklı bir etkisi yoktur
SandeepGodara

17

Birleştirmeyle ilgili birleştirme kullanmanıza yardımcı olacak bazı daha fazla ayrıntı devam eder:

Orijinal varlık dışında yönetilen bir örneğin döndürülmesi, birleştirme işleminin kritik bir parçasıdır. Kalıcılık bağlamında aynı tanıtıcıya sahip bir varlık örneği zaten varsa, sağlayıcı durumunun üzerine birleştirilen varlığın durumunun üzerine yazacaktır, ancak var olan yönetilen sürümün istemciye döndürülmesi gerekir. Kullanılmış. Sağlayıcı, Çalışan örneğini kalıcılık bağlamında güncellemediyse, söz konusu örneğe yapılan tüm başvurular birleştirilen yeni durumla tutarsız hale gelir.

Merge () yeni bir varlıkta çağrıldığında, sürekli () işlemine benzer şekilde davranır. Varlığı kalıcılık bağlamına ekler, ancak orijinal varlık örneğini eklemek yerine yeni bir kopya oluşturur ve bunun yerine bu örneği yönetir. Merge () işlemi tarafından oluşturulan kopya, sürekli () yöntemi çağrılmış gibi devam eder.

İlişkilerin varlığında, merge () işlemi yönetilen varlığı, bağımsız varlık tarafından başvurulan varlıkların yönetilen sürümlerini gösterecek şekilde güncellemeye çalışır. Varlığın kalıcı kimliği olmayan bir nesne ile ilişkisi varsa, birleştirme işleminin sonucu tanımlanmamıştır. Bazı sağlayıcılar yönetilen kopyanın kalıcı olmayan nesneyi işaret etmesine izin verirken, diğerleri hemen bir istisna atabilir. Bir istisna oluşmasını önlemek için bu durumlarda merge () işlemi isteğe bağlı olarak basamaklandırılabilir. Birleştirme () işleminin bu bölümün ilerleyen bölümlerinde anlatılacağız. Birleştirilen varlık kaldırılan bir varlığa işaret ediyorsa, IllegalArgumentException istisnası atılır.

Tembel yükleme ilişkileri, birleştirme işleminde özel bir durumdur. Tembel yükleme ilişkisi, varlık ayrılmadan önce tetiklenmediyse, varlık birleştirildiğinde bu ilişki yok sayılır. İlişki yönetilirken tetiklendiyse ve daha sonra varlık ayrılırken null değerine ayarlanırsa, kuruluşun yönetilen sürümü de birleştirme sırasında ilişkinin silinmesini sağlar. "

Yukarıdaki bilgilerin tamamı Mike Keith ve Merrick Schnicariol tarafından "Pro JPA 2 Java ™ Kalıcılık API'sında Mastering" den alınmıştır. Bölüm 6. Bölüm ayırma ve birleştirme. Bu kitap aslında yazarlar tarafından JPA'ya ayrılmış ikinci bir kitaptır. Bu yeni kitap eskisinden çok yeni bilgiye sahiptir. JPA ile ciddi bir şekilde ilgilenecek olanlar için bu kitabı gerçekten tavsiye ettim. İlk cevabımı anonim olarak gönderdiğim için üzgünüm.


17

mergeVe arasında daha fazla fark varpersist (Yine o zaten burada yayınlanan numaralandırma):

D1. mergeiletilen varlığı yönetmez, yönetilen başka bir örneği döndürür. persistdiğer tarafta, geçen varlığı yönetir:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Bir varlığı kaldırır ve sonra varlığı devam ettirmeye karar verirseniz, bunu yalnızca persist () ile yapabilirsiniz, çünkü mergebir IllegalArgumentException.

D3. Kimliklerinize el ile bakmaya karar verdiyseniz (örn. UUID'leri kullanarak), bir merge işlem SELECTbu kimliğe sahip varlıkları aramak için sonraki sorguları tetiklerken , persistbu sorgulara gerek duymayabilir.

D4. Kodunuzu çağıran koda güvenmediğiniz durumlar vardır ve hiçbir verinin güncellenmediğinden emin olunur, ancak eklenir persist.


8

Oturumda olan tembel yüklü bir koleksiyona erişmeye çalıştığım için, varlığım üzerinde özel durumları istisna alıyordum.

Ne yapacağım ayrı bir istek oldu, oturumdan varlık almak ve sonra sorunlu jsp sayfamda bir koleksiyon erişmeye çalışın.

Bunu hafifletmek için, denetleyicimdeki aynı varlığı güncelledim ve jsp'ye geçtim, ancak oturumda yeniden kaydettiğimde, yine de erişilebilir olacağını SessionScopeve birLazyLoadingException bir örnek 2 modifikasyonu :

Aşağıdakiler benim için çalıştı:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!

7

Bu açıklamayı Hazırda Bekletme belgelerinden aydınlatıcı buldum, çünkü bir kullanım durumu içeriyorlar:

Merge () kullanımı ve semantiği yeni kullanıcılar için kafa karıştırıcı görünmektedir. İlk olarak, başka bir yeni varlık yöneticisinde bir varlık yöneticisine yüklenen nesne durumunu kullanmaya çalışmadığınız sürece, merge () kullanmanız gerekmez . Bazı uygulamaların hiçbiri bu yöntemi kullanmayacaktır.

Genellikle merge () aşağıdaki senaryoda kullanılır:

  • Uygulama, ilk varlık yöneticisine bir nesne yükler
  • nesne sunum katmanına aktarılır
  • nesnede bazı değişiklikler yapılır
  • nesne iş mantığı katmanına geri aktarılır
  • uygulama, ikinci bir varlık yöneticisinde merge () yöntemini çağırarak bu değişikliklere devam eder

İşte merge () 'nin tam semantiği:

  • şu anda kalıcılık bağlamıyla ilişkilendirilmiş aynı tanımlayıcıya sahip bir yönetilen örnek varsa, verilen nesnenin durumunu yönetilen örneğe kopyalayın
  • şu anda kalıcılık bağlamıyla ilişkilendirilmiş yönetilen bir örnek yoksa, veritabanından yüklemeyi deneyin veya yeni bir yönetilen örnek oluşturun
  • yönetilen örnek döndürülür
  • verilen örnek kalıcılık bağlamıyla ilişkilendirilmez, bağımsız kalır ve genellikle atılır

Gönderen: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

Cevapları incelerken `` Çağlayan '' ve kimlik oluşturma ile ilgili bazı ayrıntılar eksik. Soruya bakın

Ayrıca, Cascadebirleştirmek ve devam etmek için ayrı ek açıklamalara sahip olabileceğinizi belirtmek gerekir : Cascade.MERGEveCascade.PERSIST kullanılan yönteme göre işlenecek.

Spec senin arkadaşın;)


6

JPA, Java platformu üzerine kurulmuş kurumsal uygulamaların alanında tartışmasız büyük bir basitleştirmedir. J2EE'deki eski varlık fasulyelerinin incelikleriyle başa çıkmak zorunda olan bir geliştirici olarak, Java EE spesifikasyonları arasında JPA'nın dahil edilmesini büyük bir sıçrama olarak görüyorum. Ancak, JPA ayrıntılarını daha derinlemesine incelerken, o kadar kolay olmayan şeyler buluyorum. Bu makalede, EntityManager'ın üst üste binme davranışı sadece bir acemi için karışıklığa neden olabilecek birleştirme ve kalıcı yöntemlerin karşılaştırılması ile ilgileniyorum. Dahası, her iki yöntemi de daha genel bir yöntemin birleştirdiği özel durumlar olarak gören bir genelleme öneriyorum.

Kalıcı varlıklar

Birleştirme yönteminin aksine, sürekli yöntem oldukça basit ve sezgiseldir. Kalıcı yöntem kullanımının en yaygın senaryosu aşağıdaki gibi özetlenebilir:

"Varlık sınıfının yeni oluşturulan bir örneği persist yöntemine geçirilir. Bu yöntem döndükten sonra, varlık yönetilir ve veritabanına eklenmesi planlanır. Bu işlem, işlem başlamadan önce veya önce veya temizleme yöntemi çağrıldığında gerçekleşebilir. İşletme, PERSIST basamaklı stratejisi ile işaretlenmiş bir ilişki yoluyla başka bir kuruluşa başvuruyorsa, bu yordam da uygulanır. "

resim açıklamasını buraya girin

Şartname daha fazla ayrıntıya girer, ancak bu ayrıntıların sadece az çok egzotik durumları kapsadığı için bunları hatırlamak önemli değildir.

Varlıkları birleştirme

Kalıcılığa kıyasla, birleştirme davranışının açıklaması o kadar basit değildir. Kalıcı olması durumunda olduğu gibi ana bir senaryo yoktur ve bir programcı doğru bir kod yazmak için tüm senaryoları hatırlamak zorundadır. Bana öyle geliyor ki, JPA tasarımcıları birincil kaygısı bağımsız varlıkları ele almak olan bir yönteme sahip olmak istiyordu (öncelikle yeni oluşturulan varlıklarla ilgilenen kalıcı yöntemin aksine). Birleştirme yönteminin ana görevi, devleti bir devamlılık bağlamında yönetilen muadiline yönetilmeyen varlık (argüman olarak geçti). Ancak bu görev, genel yöntemin davranışının anlaşılabilirliğini daha da kötüleştiren birkaç senaryoya ayrılmaktadır.

JPA belirtiminden paragrafları tekrarlamak yerine, birleştirme yönteminin davranışını şematik olarak gösteren bir akış diyagramı hazırladım:

resim açıklamasını buraya girin

Peki, ne zaman kalıcı ve ne zaman birleştirme kullanmalıyım?

inat

  • Yöntemin her zaman yeni bir varlık oluşturmasını ve bir varlığı asla güncellemesini istemezsiniz. Aksi takdirde, yöntem birincil anahtar teklik ihlallerinin bir sonucu olarak bir istisna atar.
  • Toplu işlemler, varlıkları durumlu bir şekilde işleme (bkz. Ağ Geçidi modeli).
  • Verim iyileştirmesi

birleştirmek

  • Yöntemin veritabanına bir varlık eklemesini veya güncelleştirmesini istiyorsunuz.
  • Varlıkları durumsuz bir şekilde işlemek istiyorsunuz (hizmetlerde veri aktarım nesneleri)
  • Henüz oluşturulmamış ancak henüz oluşturulmamış başka bir varlığa referans verebilecek yeni bir varlık eklemek istiyorsunuz (ilişki MERGE olarak işaretlenmelidir). Örneğin, yeni veya önceden var olan bir albüme referansla yeni bir fotoğraf ekleme.

E yönetilen ile PC'nin yönetilen bir E sürümünü içeriyor mu?
GingerBeer

5

Senaryo X:

Tablo: Spitter (Bir), Tablo: Spittles (Çok) (Spittles, FK: spitter_id ile ilişkinin sahibidir)

Bu senaryo tasarruf sağlar: Aynı Spitter'a aitmiş gibi Spitter ve her iki Spittles.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Senaryo Y:

Bu, Spitter'ı kurtaracak, 2 Spittles'i kurtaracak, ancak aynı Spitter'a referans vermeyecekler!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

1
Örümcek Graig Walls'ın "Spring in Action" adlı üçüncü baskısından alınan bir nesnedir. Spitters bir şey söyleyen kişilerdir ve Spittle aslında söyledikleri şeydir. Yani bir Spitter'ın çok fazla spreyi var, onun bir Stringler listesine sahip olduğu anlamına gelir.
George Papatheodorou

1
Spring in
Action'ı

1
Aslında bir spittle ya da bir spitter ne olduğunu bilmek zorunda değilsiniz çünkü üstte Spitter bir tablo, spitter sahibi başka bir tablo .. yazılı ve bu ...
George Papatheodorou

3

Başka bir gözlem:

merge()yalnızca otomatik olarak oluşturulan bir kimliği (test edilen IDENTITYve üzerinde SEQUENCE), tablonuzda bu tür bir kimliğe sahip bir kayıt zaten varsa dikkate alır. Bu durumda merge()kaydı güncellemeye çalışacaktır. Bununla birlikte, bir kimlik yoksa veya mevcut kayıtlarla eşleşmiyorsa, merge()bunu tamamen yok sayar ve bir db'den yeni bir kayıt tahsis etmesini ister. Bu bazen çok fazla hata kaynağıdır. merge()Yeni bir kayıt için kimliği zorlamak için kullanmayın .

persist()Öte yandan, bir kimlik bile geçirmenize asla izin vermeyecektir. Hemen başarısız olur. Benim durumumda:

Nedeni: org.hibernate.PersistentObjectException: bağımsız varlık devam etmek için geçti

hibernate-jpa javadoc'un bir ipucu var:

Atar : javax.persistence.EntityExistsException - varlık zaten varsa. (Varlık zaten varsa, sürekli işlem çağrıldığında EntityExistsException atılabilir veya EntityExistsException veya başka bir PersistenceException, çalma veya yürütme sırasında fırlatılabilir.)


2
Otomatik olarak oluşturulan kimlikler kullanmıyorsanız, yeni Varlığınıza manuel olarak bir kimlik vermeniz gerekir. persist()bir kimliği olduğundan şikayet etmeyecektir, sadece aynı kimliğe sahip bir şey veritabanında olduğunda şikayet eder.
HRS

1

Buraya ne zaman kalıcı ve ne zaman birleştirme kullanılacağı konusunda tavsiye için gelmiş olabilirsiniz. . Ben duruma bağlı olduğunu düşünüyorum: yeni bir kayıt oluşturmak için ne kadar olası ve kalıcı veri almak için ne kadar zor.

Doğal bir anahtar / tanımlayıcı kullanabileceğinizi varsayalım.

  • Verilerin kalıcı olması gerekiyor, ancak arada bir kayıt var ve güncelleme gerekiyor. Bu durumda, bir süreklilik deneyebilirsiniz ve bir EntityExistsException özel durumu atarsa, onu arar ve verileri birleştirirsiniz:

    {entityManager.persist (entity)} 'yi deneyin

    catch (EntityExistsException istisnası) {/ * al ve birleştir * /}

  • Kalıcı verilerin güncellenmesi gerekir, ancak arada bir veriler için henüz kayıt yoktur. Bu durumda ararsınız ve varlık eksikse kalıcı olur:

    entity = entityManager.find (anahtar);

    if (entity == null) {entityManager.persist (entity); }

    başka {/ * birleştirme * /}

Doğal anahtarınız / tanımlayıcınız yoksa, varlığın var olup olmadığını veya nasıl aranacağını bulmak için daha zor zamanınız olacaktır.

Birleşmeler iki şekilde de ele alınabilir:

  1. Değişiklikler genellikle küçükse, yönetilen varlığa uygulayın.
  2. Değişiklikler yaygınsa, kimliği değiştirilmemiş verilerin yanı sıra kalıcı varlıktan da kopyalayın. Ardından eski içeriği değiştirmek için EntityManager :: merge () öğesini çağırın.

0

persist (varlık) tamamen yeni varlıklarla kullanılmalıdır, bunları DB'ye eklemek için (varlık DB'de zaten varsa EntityExistsException atımı olacaktır).

Eğer varlık ayrılır ve değiştirilirse, varlığı kalıcılık bağlamına sokmak için birleştirme (varlık) kullanılmalıdır.

Muhtemelen kalıcı INSERT sql deyimi üretiyor ve UPDATE sql deyimi birleştiriyor (ama emin değilim).


Bu yanlış. Yeni bir e'de birleştirme (e) çağırırsanız, kalıcı olmalıdır.
Pedro Lamarão


JPA belirtim sürüm 2.1, bölüm 3.2.7.1, ikinci madde işaretinden: "X yeni bir varlık örneği ise, yeni bir yönetilen varlık örneği X 'oluşturulur ve X durumu yeni yönetilen varlık örneği X' içine kopyalanır."
Pedro Lamarão
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.