Cách thích hợp để gắn lại các đối tượng tách rời trong Hibernate là gì?


186

Tôi có một tình huống trong đó tôi cần phải gắn lại các đối tượng tách rời vào một phiên ngủ đông, mặc dù một đối tượng có cùng danh tính CÓ THỂ đã tồn tại trong phiên, điều này sẽ gây ra lỗi.

Ngay bây giờ, tôi có thể làm một trong hai điều.

  1. getHibernateTemplate().update( obj ) Điều này hoạt động khi và chỉ khi một đối tượng không tồn tại trong phiên ngủ đông. Các ngoại lệ được đưa ra nêu rõ một đối tượng với mã định danh đã cho đã tồn tại trong phiên khi tôi cần nó sau này.

  2. getHibernateTemplate().merge( obj ) Điều này hoạt động khi và chỉ khi một đối tượng tồn tại trong phiên ngủ đông. Các ngoại lệ được đưa ra khi tôi cần đối tượng ở trong một phiên sau nếu tôi sử dụng điều này.

Với hai kịch bản này, làm thế nào tôi có thể đính kèm các phiên vào các đối tượng một cách khái quát? Tôi không muốn sử dụng các ngoại lệ để kiểm soát dòng chảy của giải pháp này, vì phải có một giải pháp thanh lịch hơn ...

Câu trả lời:


181

Vì vậy, có vẻ như không có cách nào để gắn lại một thực thể tách rời cũ kỹ trong JPA.

merge() sẽ đẩy trạng thái cũ lên DB và ghi đè lên mọi cập nhật can thiệp.

refresh() không thể được gọi trên một thực thể tách rời.

lock() không thể được gọi trên một thực thể tách rời và thậm chí nếu có thể, và nó đã gắn lại thực thể đó, gọi 'khóa' với đối số 'LockMode.NONE' ngụ ý rằng bạn đang khóa, nhưng không khóa, là phần thiết kế API trực quan nhất Tôi đã từng thấy.

Vì vậy, bạn đang bị mắc kẹt. Có một detach()phương pháp, nhưng không attach()hoặc reattach(). Một bước rõ ràng trong vòng đời đối tượng không có sẵn cho bạn.

Đánh giá theo số lượng câu hỏi tương tự về JPA, dường như ngay cả khi JPA tuyên bố có mô hình mạch lạc, thì chắc chắn nó không phù hợp với mô hình tinh thần của hầu hết các lập trình viên, những người đã bị nguyền rủa lãng phí nhiều giờ để cố gắng hiểu làm thế nào để có được JPA để làm những việc đơn giản nhất và kết thúc với mã quản lý bộ đệm trên tất cả các ứng dụng của họ.

Có vẻ như cách duy nhất để làm điều đó là loại bỏ thực thể tách rời cũ của bạn và thực hiện một truy vấn tìm kiếm có cùng id, điều đó sẽ đánh vào L2 hoặc DB.

Mik


1
Tôi tự hỏi nếu có một lý do mà thông số kỹ thuật JPA không cho phép 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.
FGreg

11
Điều này chắc chắn là KHÔNG chính xác. Từ JPwH: *Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.Có thể tìm thấy nhiều hơn trong phần 9.3.2
cwash

Các đối tượng liên tục hoạt động rất tốt, cờ bẩn được đặt dựa trên delta giữa tải ban đầu và (các) giá trị tại thời điểm flush (). Các đối tượng tách rời cần và hiện không có chức năng này. Cách để ngủ đông thực hiện là thêm một hàm băm / id bổ sung cho các đối tượng tách rời. Và giữ một ảnh chụp nhanh về trạng thái cuối cùng của đối tượng tách rời có sẵn, giống như chúng làm cho các đối tượng liên tục. Vì vậy, họ có thể tận dụng tất cả các mã hiện có và làm cho nó hoạt động cho các đối tượng tách ra. Theo cách này như @mikhailfranco đã lưu ý, chúng tôi sẽ không "đẩy trạng thái cũ lên DB và ghi đè lên bất kỳ cập nhật can thiệp nào"
tom

2
Theo javadoc Hibernate (nhưng không phải JPA), lock(LockMode.NONE)trên thực tế có thể được gọi trên một đối tượng thoáng qua và nó sẽ gắn lại thực thể vào phiên. Xem stackoverflow.com/a/3683370/14379
seanf

khóa không hoạt động với tôi: java.lang.IllegalArgumentException: thực thể không nằm trong bối cảnh tồn tại tại org.hibernate.i INTERNal.SessionImpl.lock (SessionImpl.java:3491) tại org.hibernate.i INTERNal.SessionImpl.lock (SessionImpl.lock java: 3482) tại com.github.vok.framework.DisableTransactionControlEMDelegate.lock (DB.kt)
Martin Vysny

32

Tất cả những câu trả lời này bỏ lỡ một sự phân biệt quan trọng. update () được sử dụng để (tái) đính kèm biểu đồ đối tượng của bạn vào Phiên. Các đối tượng bạn vượt qua nó là những đối tượng được thực hiện quản lý.

merge () thực sự không phải là một API đính kèm (lại). Thông báo hợp nhất () có giá trị trả về? Đó là bởi vì nó trả về cho bạn biểu đồ được quản lý, có thể không phải là biểu đồ bạn đã vượt qua. merge () là API JPA và hành vi của nó được điều chỉnh bởi thông số JPA. Nếu đối tượng bạn truyền vào để hợp nhất () đã được quản lý (đã được liên kết với Phiên) thì đó là biểu đồ Hibernate hoạt động với; đối tượng được truyền vào là cùng một đối tượng được trả về từ merge (). Tuy nhiên, nếu đối tượng bạn chuyển vào merge () bị tách ra, Hibernate tạo một biểu đồ đối tượng mới được quản lý và nó sao chép trạng thái từ biểu đồ tách rời của bạn sang biểu đồ được quản lý mới. Một lần nữa, đây là tất cả được ra lệnh và chi phối bởi thông số kỹ thuật JPA.

Xét về chiến lược chung cho "đảm bảo thực thể này được quản lý hoặc quản lý thực thể", nó phụ thuộc vào việc bạn có muốn tính đến dữ liệu chưa được chèn hay không. Giả sử bạn làm, sử dụng một cái gì đó như

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

Lưu ý rằng tôi đã sử dụng saveOrUpdate () thay vì update (). Nếu bạn không muốn dữ liệu chưa được chèn được xử lý ở đây, hãy sử dụng update () thay vì ...


3
Đây là câu trả lời đúng cho câu hỏi này - trường hợp đóng cửa!
cwash

2
Session.contains(Object)kiểm tra bằng cách tham khảo. Nếu đã có một Thực thể khác đại diện cho cùng một hàng trong phiên và bạn vượt qua một phiên bản tách rời, bạn sẽ nhận được một ngoại lệ.
djmj

Khi Session.contains(Object)kiểm tra bằng tham chiếu, nếu có một Thực thể khác đại diện cho cùng một hàng trong phiên, nó sẽ trả về sai và nó sẽ cập nhật nó.
AxelWass

19

Câu trả lời thiếu kỷ luật: Có lẽ bạn đang tìm kiếm một bối cảnh tồn tại kéo dài. Đây là một trong những lý do chính đằng sau Khung Seam ... Nếu bạn đang vật lộn để sử dụng Hibernate trong Spring nói riêng, hãy xem đoạn tài liệu này của Seam.

Câu trả lời ngoại giao: Điều này được mô tả trong các tài liệu Hibernate . Nếu bạn cần làm rõ hơn, hãy xem Phần 9.3.2 của Tính bền bỉ của Java với Hibernate có tên là "Làm việc với các đối tượng tách rời". Tôi muốn mạnh mẽ đề nghị bạn nhận được cuốn sách này nếu bạn đang làm bất cứ điều gì hơn CRUD với Hibernate.


5
Từ seamframework.org : "Sự phát triển tích cực của Seam 3 đã bị Red Hat tạm dừng." Liên kết "đoạn tài liệu này của Seam" cũng đã chết.
badbishop

14

Nếu bạn chắc chắn rằng thực thể của bạn chưa được sửa đổi (hoặc nếu bạn đồng ý bất kỳ sửa đổi nào sẽ bị mất), thì bạn có thể gắn lại nó vào phiên có khóa.

session.lock(entity, LockMode.NONE);

Nó sẽ không khóa gì, nhưng nó sẽ lấy thực thể từ bộ đệm phiên hoặc (nếu không tìm thấy ở đó) đọc nó từ DB.

Nó rất hữu ích để ngăn chặn LazyInitException khi bạn điều hướng các mối quan hệ từ một thực thể "cũ" (ví dụ như từ httpSession). Trước tiên, bạn "đính kèm lại" thực thể.

Sử dụng get cũng có thể hoạt động, ngoại trừ khi bạn được ánh xạ kế thừa (điều này sẽ tạo ra một ngoại lệ trên getId ()).

entity = session.get(entity.getClass(), entity.getId());

2
Tôi muốn liên kết lại một thực thể với phiên. Thật không may, Session.lock(entity, LockMode.NONE)thất bại với ngoại lệ: không thể liên kết lại bộ sưu tập tạm thời chưa được khởi tạo. Làm thế nào có thể khắc phục điều này?
dma_k

1
Thực tế tôi đã không hoàn toàn đúng. Sử dụng lock () gắn lại thực thể của bạn, nhưng không phải các thực thể khác bị ràng buộc với nó. Vì vậy, nếu bạn làm entity.getOtherEntity (). GetYetAnotherEntity (), bạn có thể có ngoại lệ LazyInit. Cách duy nhất tôi biết để khắc phục điều đó là sử dụng find. entity = em.find (entity.getClass (), entity.getId ();
John Rizzo

Không có Session.find()phương pháp API. Có lẽ bạn có ý nghĩa Session.load(Object object, Serializable id).
dma_k

11

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#persistphươ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 Sessionxả 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 EntityManagergiao 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:

Chuyển trạng thái thực thể JPA

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:

Chuyển trạng thái thực thể ngủ đông

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ì Bookthự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 mergephươ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 updatephương thức.

Một Hibernate Sessionchỉ 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ì Bookthực thể và chúng tôi đã sửa đổi nó khi Bookthự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 updatephương pháp đòi hỏi bạn phải unwrapEntityManagermộ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 @SelectBeforeUpdatechú 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 updatelà 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 NonUniqueObjectExceptionvì thứ hai EntityManagerđã chứa một Bookthực thể có cùng định danh với cái mà chúng ta chuyển đến updatevà 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 mergephươ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 updatenà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 mergehoạt động, do đó giảm thời gian thực hiện cập nhật hàng loạt.


Câu trả lời tốt đẹp. Tôi đã tự hỏi về @SelectBeforeUpdatechú thích mặc dù. Khi nào được chọn kích hoạt? Khi gọi update, ngay trước khi xả hoặc nó không thực sự quan trọng (có thể là vấn đề nếu ngủ đông đã tải tất cả các thực thể được chú thích trong một cuộc gọi trước khi xả)?
Andronicus

Các @SelectBeforeUpdatekích hoạt CHỌN trong flushhoạt động Bối cảnh liên tục . Kiểm tra các getDatabaseSnapshotphương pháp trongDefaultFlushEntityEventListener để biết thêm chi tiết.
Vlad Mihalcea

10

Tôi đã quay lại JavaDoc org.hibernate.Sessionvà tìm thấy như sau:

Các trường hợp thoáng qua có thể được thực hiện liên tục bằng cách gọi save(), persist()hoặc saveOrUpdate(). Các trường hợp liên tục có thể được thực hiện tạm thời bằng cách gọi delete(). Bất kỳ trường hợp nào được trả về bởi một get()hoặc load()phương thức là liên tục. Trường hợp tách ra có thể được thực hiện bằng cách gọi dai dẳng update(), saveOrUpdate(), lock()hoặc replicate(). Trạng thái của một thể hiện thoáng qua hoặc tách rời cũng có thể được thực hiện liên tục như một thể hiện liên tục mới bằng cách gọi merge().

Do đó update(), saveOrUpdate(), lock(), replicate()merge()là những tùy chọn ứng cử viên.

update(): Sẽ đưa ra một ngoại lệ nếu có một trường hợp liên tục có cùng định danh.

saveOrUpdate(): Lưu hoặc cập nhật

lock(): Không dùng nữa

replicate(): Duy trì trạng thái của cá thể tách rời đã cho, sử dụng lại giá trị định danh hiện tại.

merge(): Trả về một đối tượng liên tục có cùng định danh. Ví dụ đã cho không trở nên liên kết với phiên.

Do đó, lock()không nên được sử dụng thẳng và dựa trên yêu cầu chức năng, một hoặc nhiều trong số chúng có thể được chọn.


7

Tôi đã làm theo cách đó trong C # với NHibernate, nhưng nó sẽ hoạt động theo cách tương tự trong Java:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

Khóa đầu tiên được gọi trên mọi đối tượng vì Chứa luôn sai. Vấn đề là NHibernate so sánh các đối tượng theo id và loại cơ sở dữ liệu. Chứa sử dụng equalsphương thức, so sánh bằng tham chiếu nếu nó không bị ghi đè. Với equalsphương pháp đó, nó hoạt động mà không có bất kỳ Ngoại lệ nào:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}

4

Session.contains(Object obj) kiểm tra tham chiếu và sẽ không phát hiện một thể hiện khác đại diện cho cùng một hàng và đã được đính kèm với nó.

Đây là giải pháp chung của tôi cho các Thực thể có thuộc tính định danh.

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

Đây là một trong số ít các khía cạnh của .Net EntityFramework tôi thích, các tùy chọn đính kèm khác nhau liên quan đến các thực thể thay đổi và thuộc tính của chúng.


3

Tôi đã đưa ra một giải pháp để "làm mới" một đối tượng từ kho lưu trữ bền bỉ sẽ chiếm các đối tượng khác có thể đã được đính kèm vào phiên:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}

2

Xin lỗi, dường như không thể thêm ý kiến ​​(chưa?).

Sử dụng Hibernate 3.5.0-Final

Trong khi Session#lockphương thức này không được dùng nữa, javadoc sẽ đề xuất sử dụng Session#buildLockRequest(LockOptions)#lock(entity)và nếu bạn chắc chắn rằng các liên kết của mình có cascade=lock, thì việc tải nhanh cũng không phải là vấn đề.

Vì vậy, phương pháp đính kèm của tôi trông hơi giống

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

Các xét nghiệm ban đầu cho thấy nó hoạt động một điều trị.


2

Có lẽ nó hành xử hơi khác nhau trên Eclipselink. Để gắn lại các đối tượng tách rời mà không nhận được dữ liệu cũ, tôi thường làm:

Object obj = em.find(obj.getClass(), id);

và là một bước thứ hai tùy chọn (để bộ nhớ cache bị vô hiệu):

em.refresh(obj)

1

hãy thử getHibernateTemplate (). sao chép (thực thể, ReplicationMode.LATEST_VERSION)


1

Trong bài viết gốc, có hai phương pháp, update(obj)merge(obj)được đề cập để làm việc, nhưng trong trường hợp ngược lại. Nếu điều này thực sự đúng, thì tại sao không kiểm tra xem đối tượng đã có trong phiên trước chưa, sau đó gọi update(obj)nếu có, nếu không thì gọi merge(obj).

Các thử nghiệm cho sự tồn tại trong phiên là session.contains(obj). Do đó, tôi nghĩ mã giả sau sẽ hoạt động:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}

2
chứa () kiểm tra so sánh theo tham chiếu, nhưng các hàm ngủ đông hoạt động theo id cơ sở dữ liệu. session.merge sẽ không bao giờ được gọi trong mã của bạn.
Verena Haunschmid

1

để gắn lại đối tượng này, bạn phải sử dụng merge ();

methode này chấp nhận tham số thực thể của bạn tách ra và trả về một thực thể sẽ được đính kèm và tải lại từ Cơ sở dữ liệu.

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);

0

gọi hợp nhất đầu tiên () (để cập nhật phiên bản liên tục), sau đó khóa (LockMode.NONE) (để đính kèm phiên bản hiện tại, không phải phiên bản được trả về bởi merge ()) dường như hoạt động đối với một số trường hợp sử dụng.


0

Tài sản hibernate.allow_refresh_detached_entityđã lừa tôi. Nhưng nó là một quy tắc chung, vì vậy nó không phù hợp lắm nếu bạn chỉ muốn làm điều đó trong một số trường hợp. Tôi hy vọng nó sẽ giúp.

Đã thử nghiệm trên Hibernate 5.4.9

PhiênFactoryOptionsBuilder



Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.