2011-01-18 36 views
11

Tôi có hai entity bean được định nghĩa như sau (thứ không liên quan bị loại bỏ):Dừng Hibernate từ việc cập nhật các bộ sưu tập khi họ đã không thay đổi

@Entity 
@Table(...) 
public class MasterItem implements java.io.Serializable { 

    private Set<CriticalItems> criticalItemses = new HashSet<CriticalItems>(0); 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true, 
      cascade = {javax.persistence.CascadeType.DETACH}) 
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE}) 
    public Set<CriticalItems> getCriticalItemses() { 
     return this.criticalItemses; 
    } 
} 

CriticalItems được định nghĩa như sau:

@Entity 
@Table(...) 
public class CriticalItems implements java.io.Serializable { 

    private MasterItem masterItem; 

    @ManyToOne(fetch = FetchType.LAZY, optional = false, 
      cascade = {javax.persistence.CascadeType.DETACH}) 
    @Cascade({CascadeType.SAVE_UPDATE}) 
    @JoinColumn(name = "mi_item_id", nullable = false) 
    public MasterItem getMasterItem() { 
     return this.masterItem; 
    } 
} 

Và trong tôi Mã DAO - Tôi có các phương pháp sau:

public MasterItem load(int id) { 
    MasterItem results = (MasterItem) getSessionFactory().getCurrentSession() 
     .get("com.xxx.MasterItem", id); 

} 

public void save(MasterItem master) { 
    // master has been changed by the UI since it 
    getSessionFactory().getCurrentSession().saveOrUpdate(master); 
} 

Khi tôi tải MasterItem, nó tải đúng, một d cũng tải CriticalItems Set với dữ liệu, theo chỉ dẫn. Sau đó, tôi gửi dữ liệu này đến giao diện người dùng của mình và nhận lại bản sao được cập nhật mà sau đó tôi cố gắng tiếp tục tồn tại. Người dùng cập nhật các trường trong đối tượng MasterItem, nhưng không chạm vào tập CriticalItems hoặc bất kỳ thứ gì trong nó - nó vẫn chưa được sửa đổi.

Khi phương thức save() của tôi được gọi, Hibernate sẽ nhấn mạnh việc gửi các bản cập nhật SQL cho mỗi mục trong Set of CriticalItems, mặc dù không ai trong số chúng thay đổi theo bất kỳ cách nào.

Sau một số lần đào, đây là những gì tôi nghĩ đang xảy ra. Khi tôi thực hiện saveOrUpdate(), Hibernate thấy đối tượng MasterItem của tôi ở trạng thái tách rời, vì vậy nó sẽ cố tải lại nó từ đĩa. Tuy nhiên, khi làm như vậy, nó dường như đang sử dụng một câu lệnh chuẩn bị (được tạo tự động bởi Hibernate lúc khởi động) và câu lệnh đã được chuẩn bị này không cố gắng tham gia vào dữ liệu CriticalItems. Vì vậy, Hibernate có đối tượng MasterItem cập nhật của tôi với một tập hợp đầy đủ các CriticalItems, nhưng sử dụng một MasterItem mà không có các bộ sưu tập như là đối tượng "previousState" của nó. Vì vậy, tất cả CriticalItems được cập nhật thông qua SQL (không được chèn vào, điều thú vị trong chính nó).

Tôi có làm điều gì đó trong chú thích đã gây ra hành vi này không? Tôi biết tôi có thể sử dụng một Interceptor để tìm ra item đã thực sự thay đổi, hoặc thay đổi cờ bẩn để ghi đè thuật toán mặc định của Hibernate - nhưng điều này có vẻ là một cái gì đó mà Hibernate chỉ nên xử lý mà không cần sự can thiệp của tôi.

Mọi thông tin chi tiết sẽ được đánh giá cao.

CẬP NHẬT: Dựa trên nhận xét, tôi nghĩ rằng tôi hiểu sự khác biệt giữa saveOrUpdate() và hợp nhất(). Tôi nhận ra rằng saveOrUpdate() sẽ dẫn đến SQL INSERT hoặc SQL UPDATE trong mọi trường hợp, và hợp nhất, về mặt lý thuyết, sẽ chỉ cập nhật nếu đối tượng đã thay đổi từ trạng thái liên tục, nhưng để xác định điều đó, Hibernate phải tải lại đối tượng đầu tiên thông qua SQL SELECT. Vì vậy, tôi nghĩ rằng tôi chỉ có thể quay trở lại vào mã của tôi và thay đổi saveOrUpdate() để hợp nhất() và nó sẽ làm việc, nhưng đó không phải là hoàn toàn như vậy.

Khi tôi sử dụng kết hợp(), tôi đã nhận được

org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session 

nhưng nó làm việc tốt nếu tôi đổi lại thành saveOrUpdate().

Cuối cùng tôi đã tìm ra lý do - tôi không bao gồm CascadeType.MERGE trong chú thích @Cascade của mình (ugh). Khi tôi đã sửa nó, ngoại lệ đã biến mất.

+0

bạn có cột khóa lạc quan không (cột được chú thích với @version với kiểu ngày tháng) – lweller

Trả lời

16

Đó là sự khác biệt ngữ nghĩa giữa update()merge().

Từ Christian Bauer and Gavin King's Java Persistence with Hibernate (Tôi không thể tìm thấy lời giải thích rõ ràng về hành vi này trong các tài liệu Hibernate):

Bản cập nhật() phương pháp buộc một bản cập nhật để tình trạng kéo dài của đối tượng trong cơ sở dữ liệu, luôn luôn lên lịch cập nhật SQL.
...
Không quan trọng nếu đối tượng mục được sửa đổi trước hoặc sau khi đối tượng được chuyển đến cập nhật().
...
Hibernate luôn đối xử với đối tượng là dơ bẩn và lên lịch cập nhật SQL, sẽ được thực thi trong khi xả.

Mặt khác, merge() truy vấn cơ sở dữ liệu trước và không thực hiện cập nhật nếu trạng thái không thay đổi. Vì vậy, nếu bạn muốn Hibernate truy vấn cơ sở dữ liệu trước, bạn cần sử dụng merge() (mặc dù hành vi mặc định là update() có thể được ghi đè bằng cách chỉ định @org.hibernate.annotations.Entity(selectBeforeUpdate = true) trên các thực thể của bạn).

+0

Chính xác những gì tôi đang tìm kiếm - cảm ơn một triệu! – Jay

+0

FYI - Tôi đã chạy vào [bài đăng này để giải thích thêm về sự khác biệt] (http://stackoverflow.com/questions/170962/nhibernate-difference-between-session-merge-and-session-saveorupdate) giữa update() hoặc saveOrUpdate() và hợp nhất(). – Jay

1

cố gắng thêm một cột sửa đổi (lạc khóa) cho các đối tượng của bạn

@Version 
Date lastModified; 
+0

FYI - điều này có thể hữu ích, nhưng tôi không có cột phiên bản trong cơ sở dữ liệu của mình. – Jay

Các vấn đề liên quan