2009-07-30 44 views
8

Tôi gặp vấn đề này trong một thời gian dài, tôi đã tìm kiếm trên web và SO vào và ra và không tìm thấy giải pháp nào. Tôi hy vọng bạn có thể giúp tôi về điều đó.Hibernate @OneToMany với mối quan hệ mappedBy (cha mẹ con) và vấn đề bộ nhớ cache

Tôi có một mối quan hệ cha-con giữa hai thực thể như sau:

@Entity 
public class Parent { 
    // ... 

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) 
    private Set<Child> children = new HashSet<Child>(); 

    // ... 
} 

@Entity 
public class Child { 
    // ... 

    @ManyToOne(fetch = FetchType.LAZY) 
    private Parent parent; 

    // ... 
} 

Điều được rằng khi tôi tạo ra một đứa trẻ mới và gán nó vào một phụ huynh, phụ huynh không được cập nhật khi nó đã có trong cache rồi.

Parent parent = new Parent(); 
em.persist(parent); 

// ... 

Child child = new Child(); 
child.setParent(parent); 
em.persist(child); 

parent.getChildren().size(); // returns 0 

Tôi đã cố gắng sử dụng @PreUpdate để tự động thêm đứa trẻ cho phụ huynh khi trẻ được tồn, nhưng trong trường hợp khi chúng tôi có 2 nhà quản lý thực thể trong 2 chủ đề khác nhau (như trong JBoss), vấn đề này vẫn tồn tại, cho đến khi chúng tôi gọi em.refresh(parent)

Vì vậy, câu hỏi đặt ra là - có cách nào để loại bỏ vấn đề một cách suôn sẻ và đảm bảo rằng parent.getChildren() luôn trả về danh sách cập nhật của trẻ em không?

Trả lời

8

Hầu hết ORM sẽ hoạt động theo cách này.

Đối tượng trong bộ nhớ cache không được cập nhật từ cơ sở dữ liệu (đọc thêm không cần thiết). Cũng nghĩ về mô hình đối tượng và sự bền bỉ như riêng biệt. tức là giữ cho mô hình đối tượng của bạn nhất quán với chính nó và không dựa vào cơ chế kiên trì để làm điều này cho bạn.

Vì vậy, nếu bạn muốn đối tượng được thêm vào bộ sưu tập thì hãy làm điều đó trong mã "setParent".

Thực tiễn tốt nhất trong trường hợp này là để làm cho một bên của mối quan hệ thực hiện tất cả công việc và để bên kia trì hoãn nó. Ngoài ra tôi sẽ đề nghị sử dụng truy cập trường hơn là truy cập phương pháp, theo cách đó bạn có thể tùy chỉnh các phương thức linh hoạt hơn.

Thêm một phương pháp để cha mẹ gọi addChild

public void addChild(Child child) { 
    child.setParent0(this); 
    getChildren().add(individualNeed); 
} 

và sau đó làm cho setParent ở trẻ em:

public void setParent(Parent parent) { 
    parent.addChild(child); 
} 

setParent0 ở trẻ em là stter tài sản cho cha mẹ về con.

public void setParent0(Parent parent) { 
    this.parent = parent; 
} 

Tôi cũng xin đề nghị rằng phương pháp "getChildren" trả về một bộ sưu tập bất biến để phát triển không inadvertantly không sử dụng phương pháp này (tôi đã học cách cứng trong tất cả điều này).

Một điều nữa, bạn nên có mã kiểm tra không có giá trị và các phần phòng thủ khác trong mã ở trên, tôi đã bỏ nó ra để rõ ràng.

+0

Cảm ơn bạn đã trả lời sâu rộng của bạn, Michael. Tôi đã tìm thấy một số thông tin tốt trong đó. Nhưng, tôi e ngại, nó không giải quyết được vấn đề tôi có bởi vì hai cá thể EntityManager khác nhau giữ hai bộ đệm khác nhau và khi một trong số chúng cập nhật một cá thể thực thể, một cái khác không nhìn thấy bản cập nhật và thực thể được lưu trong bộ nhớ cache trở nên lỗi thời – artemb

+0

Có vẻ như bạn cần phải xem xét các trình kích hoạt cập nhật mà sau đó sẽ lấy đối tượng đó và cập nhật bộ nhớ cache khác. Hoặc bạn có thể làm cho hai người lưu trữ thành viên của cùng một cụm nếu bạn lưu trữ bộ nhớ đệm giải pháp hỗ trợ phân cụm. –

+0

Thật không may tôi không có quyền kiểm soát bộ nhớ cache phiên của Hibernate. Hay tôi? – artemb

1

Về vấn đề của bạn với bộ nhớ đệm, đây là một vấn đề rất phổ biến khi bạn có nhiều máy ảo chạy trên cùng cơ sở dữ liệu với bộ đệm riêng biệt. Nó được gọi là "cache drift".

Hầu hết các triển khai bộ nhớ cache thân thiện với hibernate (ehcache, OSCache và SwarmCache) đều có bộ nhớ đệm được tích hợp sẵn có thể được sử dụng để đồng bộ hóa bộ đệm. Nói chung, cache được phân phối gửi các tin nhắn multicast cập nhật trạng thái của cache. Ví dụ, việc loại bỏ bộ nhớ cache cấp thứ hai theo SessionFactory.evict (Class, id) sẽ làm cho một thông báo không hợp lệ được gửi tới các bộ đệm khác trong cụm sẽ làm mất hiệu lực bất kỳ bản sao nào khác của đối tượng đó trong bộ đệm khác.

Tùy thuộc vào việc triển khai của bạn, quảng cáo đa phương tiện có thể hoặc không thể chấp nhận cho bạn. Nếu nó không phải là bạn có thể cần phải sử dụng một giải pháp bộ nhớ cache đơn như memcached.

Cá nhân tôi đã tìm thấy cấu hình bộ nhớ cache được phân phối của bộ nhớ cache của eh rất đơn giản.

EH bộ nhớ cache thảo luận về những vấn đề trong một chút chi tiết hơn ở đây: http://ehcache.org/documentation/distributed_caching.html

4

Đẹp, chắc chắn vấn đề của bạn ở đây là thiết lập Cascade của bạn.

@Entity 
public class Parent { 
    // ... 

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, 
     cascade = {CascadeType.REMOVE, CascadeType.PERSIST}) 
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE}) 
    private Set<Child> children = new HashSet<Child>(); 

    // ... 
} 

@Entity 
public class Child { 
    // ... 

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) 
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE}) 
    private Parent parent; 

    // ... 
} 

Sử dụng các cài đặt xếp tầng này sẽ tiếp tục tồn tại và cập nhật đối tượng con.

ví dụ: Thông tin

Parent parent = new Parent(); 
em.persist(parent); 

// ... 

Child child = new Child(); 
child.setParent(parent); 
em.persist(child); //will cascade update to parent 

parent.getChildren().size(); // returns 1 

hoặc

Parent parent = new Parent(); 
Child child = new Child(); 
parent.setChild(parent); 
em.persist(parent); //will cascade update to child 

child.getParent(); // returns the parent 

hơn về vấn đề này có thể được tìm thấy tại Hibernate Annotations

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