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#persist
yö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 Session
yı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, EntityManager
arabirim 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 EntityManager
için birleştirme işlemini kullanabilirsiniz .
Yerel Hazırda Bekletme API'sını kullanırken, ayrı merge
bir 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 Book
varlığı sürdürdüğümüzü düşünün ve şimdi işletme, EntityManager
iş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 merge
yö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 update
yö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 Book
ettiğimizi ve Book
iş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
update
Yöntem gerektirir bir hazırda için .unwrap
EntityManager
Session
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 EntityManager
zaten Book
geçtiğimizle aynı tanımlayıcıya sahip bir varlık içerdiğinden update
ve 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ç
merge
Yö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 update
tarafı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.