Vì đây là một câu hỏi rất phổ biến, tôi đã viết
bài viết này , trên đó câu trả lời này dựa trên.
Thực thể
JPA định nghĩa các trạng thái thực thể sau:
Mới (thoáng qua)
Một đối tượng mới được tạo chưa từng được liên kết với Hibernate Session
(aka Persistence Context
) và không được ánh xạ tới bất kỳ hàng trong bảng cơ sở dữ liệu nào được coi là ở trạng thái Mới (Tạm thời).
Để trở nên bền bỉ, chúng ta cần gọi EntityManager#persist
phương thức một cách rõ ràng hoặc sử dụng cơ chế duy trì bắc cầu.
Kiên trì (Quản lý)
Một thực thể liên tục đã được liên kết với một hàng của bảng cơ sở dữ liệu và nó được quản lý bởi Bối cảnh liên tục hiện đang chạy. Bất kỳ thay đổi nào được thực hiện cho một thực thể như vậy sẽ được phát hiện và lan truyền đến cơ sở dữ liệu (trong thời gian tuôn ra Phiên).
Với Hibernate, chúng ta không còn phải thực thi các câu lệnh INSERT / UPDATE / DELETE. Hibernate sử dụng một phong cách làm việc đằng sau giao dịch và các thay đổi được đồng bộ hóa tại thời điểm chịu trách nhiệm cuối cùng, trong thời gian Session
xả hiện tại .
Tách rời
Khi Bối cảnh liên tục hiện đang chạy, tất cả các thực thể được quản lý trước đó sẽ bị tách ra. Những thay đổi kế tiếp sẽ không còn được theo dõi và sẽ không xảy ra đồng bộ hóa cơ sở dữ liệu tự động.
Thực thể chuyển trạng thái
Bạn có thể thay đổi trạng thái thực thể bằng các phương thức khác nhau được xác định bởi EntityManager
giao diện.
Để hiểu rõ hơn về quá trình chuyển đổi trạng thái thực thể JPA, hãy xem xét sơ đồ sau:
Khi sử dụng JPA, để liên kết lại một thực thể tách rời thành một hoạt động EntityManager
, bạn có thể sử dụng thao tác hợp nhất .
Khi sử dụng API Hibernate gốc, ngoài ra merge
, bạn có thể gắn lại một thực thể tách rời thành một Phiên bản Hibernate đang hoạt động Sử dụng các phương thức cập nhật, như được minh họa bằng sơ đồ sau:
Sáp nhập một thực thể tách rời
Việc hợp nhất sẽ sao chép trạng thái thực thể tách rời (nguồn) sang một thực thể được quản lý (đích).
Hãy xem xét chúng tôi đã duy trì Book
thực thể sau và bây giờ thực thể được tách ra như thực thể EntityManager
được sử dụng để duy trì thực thể đã bị đóng:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
Trong khi thực thể ở trạng thái tách rời, chúng tôi sửa đổi nó như sau:
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
Bây giờ, chúng tôi muốn tuyên truyền các thay đổi cho cơ sở dữ liệu, vì vậy chúng tôi có thể gọi merge
phương thức:
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Và Hibernate sẽ thực thi các câu lệnh SQL sau:
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
Nếu thực thể hợp nhất không có tương đương trong hiện tại EntityManager
, ảnh chụp thực thể mới sẽ được tìm nạp từ cơ sở dữ liệu.
Khi có một thực thể được quản lý, JPA sao chép trạng thái của thực thể tách rời sang thực thể hiện đang được quản lý và trong bối cảnh Kiên trìflush
, một CẬP NHẬT sẽ được tạo nếu cơ chế kiểm tra bẩn phát hiện ra rằng thực thể được quản lý đã thay đổi.
Vì vậy, khi sử dụng merge
, đối tượng tách rời sẽ tiếp tục được tách ra ngay cả sau khi hoạt động hợp nhất.
Gắn lại một thực thể tách rời
Hibernate, nhưng không JPA hỗ trợ gắn lại thông qua update
phương thức.
Một Hibernate Session
chỉ có thể liên kết một đối tượng thực thể cho một hàng cơ sở dữ liệu nhất định. Điều này là do Bối cảnh liên tục hoạt động như một bộ đệm trong bộ nhớ (bộ đệm cấp đầu tiên) và chỉ có một giá trị (thực thể) được liên kết với một khóa nhất định (loại thực thể và định danh cơ sở dữ liệu).
Một thực thể chỉ có thể được gắn lại nếu không có đối tượng JVM khác (khớp với cùng một hàng cơ sở dữ liệu) đã được liên kết với Hibernate hiện tại Session
.
Xem xét chúng tôi đã duy trì Book
thực thể và chúng tôi đã sửa đổi nó khi Book
thực thể ở trạng thái tách rời:
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"
);
Chúng ta có thể gắn lại thực thể tách rời như thế này:
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Và Hibernate sẽ thực thi câu lệnh SQL sau:
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Các update
phương pháp đòi hỏi bạn phải unwrap
là EntityManager
một Hibernate Session
.
Không giống như merge
, thực thể tách rời được cung cấp sẽ được liên kết lại với Bối cảnh liên tục hiện tại và một CẬP NHẬT được lên lịch trong quá trình xóa xem thực thể có sửa đổi hay không.
Để ngăn chặn điều này, bạn có thể sử dụng @SelectBeforeUpdate
chú thích Hibernate sẽ kích hoạt câu lệnh CHỌN tìm nạp trạng thái được tải sau đó được sử dụng bởi cơ chế kiểm tra bẩn.
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Cảnh giác với NonUniqueObjectException
Một vấn đề có thể xảy ra update
là nếu Bối cảnh liên tục đã chứa tham chiếu thực thể có cùng id và cùng loại như trong ví dụ sau:
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
);
}
Bây giờ, khi thực hiện trường hợp thử nghiệm ở trên, Hibernate sẽ ném một NonUniqueObjectException
vì thứ hai EntityManager
đã chứa một Book
thực thể có cùng định danh với cái mà chúng ta chuyển đến update
và Bối cảnh liên tục có thể chứa hai biểu diễn của cùng một thực thể.
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)
Phần kết luận
Các merge
phương pháp là được ưa chuộng hơn nếu bạn đang sử dụng khóa lạc quan vì nó cho phép bạn ngăn chặn bản cập nhật bị mất. Để biết thêm chi tiết về chủ đề này, hãy xem bài viết này .
Điều update
này tốt cho các cập nhật hàng loạt vì nó có thể ngăn chặn câu lệnh CHỌN bổ sung được tạo bởi merge
hoạt động, do đó giảm thời gian thực hiện cập nhật hàng loạt.
refresh()
trên các thực thể tách rời? Nhìn qua thông số kỹ thuật 2.0 tôi không thấy bất kỳ lời biện minh nào; chỉ là nó không được phép.