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 EntityManager
aşağıdaki varlık durumu geçiş yöntemlerini sağlar.
Hazırda Beklet, Session
tüm JPA EntityManager
yöntemlerini uygular ve save
, saveOrUpdate
ve gibi bazı ek varlık durumu geçiş yöntemleri sağlar update
.
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 persist
JPA tarafından sunulan yöntemi kullanabiliriz .EntityManager
Session
persist
Yöntem, bir tetikler PersistEvent
ile kullanılan ve DefaultPersistEventListener
hazı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 id
eklenmeden ö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 EntityManager
ve Hazırda Bekletme'nin Session
Birinci 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ü save
yöntem JPA'dan önce gelir ve Hazırda Bekletme projesinin başından beri kullanılabilir.
save
Yöntem, bir tetikler SaveOrUpdateEvent
ile kullanılan ve DefaultSaveOrUpdateEventListener
hazırda olay dinleyicisi. Bu nedenle, save
yöntem update
ve saveOrUpdate
yöntemlerine eşdeğerdir .
save
Yö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ç persist
yöntem çağrısıyla aynıdır . Ancak, aksine persist
, save
yö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 update
yöntem, kirli denetim mekanizmasını atlamak ve bir varlık sifonunu güncellemeye zorlamak içindir.
update
Yöntem, bir tetikler SaveOrUpdateEvent
ile kullanılan ve DefaultSaveOrUpdateEventListener
hazırda olay dinleyicisi. Bu nedenle, update
yöntem save
ve saveOrUpdate
yöntemlerine eşdeğerdir .
update
Yö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 update
yö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
UPDATE
Devamlı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 entity
mesaj günlüğe kaydedilir.
@SelectBeforeUpdate
Gereksiz 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 @SelectBeforeUpdate
bir SELECT
ifadeyi tetikleyecek Hazırda Bekletme notunu kullanabilirsiniz.loaded state
ve daha sonra kirli kontrol mekanizması tarafından kullanılan .
Dolayısıyla, Book
varlığı @SelectBeforeUpdate
ek 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, UPDATE
Hazı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ü saveOrUpdate
yöntem sadece için takma save
ve update
.
saveOrUpdate
Yöntem, bir tetikler SaveOrUpdateEvent
ile kullanılan ve DefaultSaveOrUpdateEventListener
hazırda olay dinleyicisi. Bu nedenle, update
yöntem save
ve saveOrUpdate
yöntemlerine eşdeğerdir .
Şimdi, saveOrUpdate
bir varlığı devam ettirmek veya UPDATE
aş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
, update
ve saveOrUpdate
Sebat 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 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)
Birleştirmek
Bundan kaçınmak için NonUniqueObjectException
, merge
JPA tarafından sunulan EntityManager
ve Hazırda Beklet tarafından devralınan yöntemi kullanmanız gerekir Session
.
Açıklandığı gibi bu makalede , merge
hiç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 merge
yöntemle.
merge
Yöntem, bir tetikler MergeEvent
ile kullanılan ve DefaultMergeEventListener
hazırda olay dinleyicisi.
merge
Yö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ığı merge
bir 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
, merge
yönteme ilettiğimiz müstakil referanstan farklı olduğuna dikkat edin .
Şimdi, merge
ayrılmış varlık durumunu kopyalarken JPA kullanmayı tercih etmenize rağmen , SELECT
toplu işleme görevini yürütürken fazlalık sorunlu olabilir.
Bu nedenle, update
o 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 persist
yöntemini kullanmanız gerekir . Müstakil varlık durumunu kopyalamak için merge
tercih edilmelidir. update
Yöntem toplu işlemler, sadece için yararlıdır. save
Ve saveOrUpdate
sadece rumuzlarıdırlar update
ve 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 .