Bu çok yaygın bir soru olduğundan, bu cevabın dayandığı bu makaleyi yazdım
.
Varlık durumları
JPA aşağıdaki varlık durumlarını tanımlar:
Yeni (Geçici)
Bir Hazırda Bekletme Session(aka Persistence Context) ile ilişkilendirilmemiş ve herhangi bir veritabanı tablosu satırıyla eşlenmemiş yeni oluşturulan bir nesnenin 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. Bu tür bir varlıkta 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ığında, ö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şmeyecektir.
Varlık durumu geçişleri
Varlık durumunu, EntityManagerarabirim tarafından tanımlanan çeşitli yöntemleri kullanarak değiştirebilirsiniz .
JPA varlık durumu geçişlerini daha iyi anlamak için aşağıdaki diyagramı göz önünde bulundurun:

JPA kullanırken, ayrılmış bir objeyi bir aktifle yeniden ilişkilendirmek EntityManageriçin birleştirme işlemini kullanabilirsiniz .
Yerel Hazırda Bekletme API'sını kullanırken, ayrı mergebir varlığı aşağıdaki diyagramda gösterildiği gibi güncelleme yöntemlerini kullanarak etkin bir Hazırda Bekletme Oturumuna yeniden bağlayabilirsiniz:

Ayrılmış bir varlığı birleştirme
Birleştirme, ayrılmış varlık durumunu (kaynak) yönetilen varlık örneğine (hedef) kopyalayacaktır.
Aşağıdaki Bookvarlığı sürdürdüğümüzü düşünün ve şimdi işletme, EntityManagerişletmenin kapanmasına devam etmek için kullanıldığından ayrılmıştır :
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
Varlık müstakil durumdayken, onu aşağıdaki gibi değiştiririz:
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Şimdi, değişiklikleri veritabanına yaymak istiyoruz, böylece mergeyöntemi çağırabiliriz :
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Hazırda Bekletme, aşağıdaki SQL deyimlerini yürütür:
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
Birleştirilen varlığın geçerli eşdeğeri yoksa EntityManager, veritabanından yeni bir varlık anlık görüntüsü alınır.
Yönetilen bir varlık olduğunda, JPA ayrılmış varlığın durumunu şu anda yönetilene kopyalar ve Kalıcılık Bağlamıflush sırasında , kirli kontrol mekanizması yönetilen varlığın değiştiğini tespit ederse bir UPDATE oluşturulur .
Bu nedenle, kullanıldığında merge, ayrılmış nesne örneği birleştirme işleminden sonra bile ayrılmaya devam eder.
Ayrılmış bir varlığın yeniden takılması
Hazırda bekletme, ancak JPA updateyöntemde yeniden eklemeyi desteklemez .
Hazırda Bekletme Session, 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 ile ilişkilendirilmiş başka bir JVM nesnesi (aynı veritabanı satırıyla eşleşen) yoksa yeniden bağlanabilir Session.
İşletmeyi ısrar Bookettiğimizi ve Bookişletme ayrık durumdayken değiştirdiğimizi dikkate alarak :
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Ayrılmış varlığı şu şekilde yeniden bağlayabiliriz:
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Ve Hazırda Beklet aşağıdaki SQL deyimini yürütür:
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
updateYöntem gerektirir bir hazırda için .unwrapEntityManagerSession
Aksine merge, sağlanan müstakil varlık mevcut Kalıcılık Bağlamıyla yeniden ilişkilendirilir ve varlığın değiştirilip değiştirilmediğine bakılmak üzere bir UPDATE programlanır.
Bunu önlemek için @SelectBeforeUpdate, daha sonra kirli kontrol mekanizması tarafından kullanılan yüklü durumu getiren bir SELECT deyimini tetikleyecek Hazırda Bekletme notunu kullanabilirsiniz .
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
NonUniqueObjectException özelliğine dikkat edin
Oluşabilecek bir sorun update, Kalıcılık Bağlamının aşağıdaki örnekte olduğu gibi aynı kimliğe ve aynı türe sahip bir varlık başvurusu içermesidir:
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)
Sonuç
mergeYöntem kayıp güncellemeleri engellemek etme olanağı sağladığından iyimser kilitleme kullanıyorsanız tercih edilmelidir. Bu konu hakkında daha fazla bilgi için bu makaleye göz atın .
İşlem updatetarafından oluşturulan ek SELECT deyimini engelleyebileceğinden merge, toplu güncelleştirme yürütme süresini azaltabileceği için toplu güncelleştirmeler için iyidir .
refresh()müstakil varlıklara izin vermiyor bir nedeni var mı? 2.0 spec bakarak herhangi bir gerekçe görmüyorum; sadece izin verilmiyor.