2009-07-30 155 views
8

我有這個問題很長一段時間了,我已經搜索了網絡和SO進出,並沒有找到解決方案。我希望你能幫助我。Hibernate @OneToMany與mappedBy(父子)關係和緩存問題

我有這樣兩個實體之間的父子關係如下:

@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; 

    // ... 
} 

的事情是,當我創建一個新的子並將其分配給家長,家長沒有更新時它已經在緩存中了。

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

// ... 

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

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

我曾嘗試使用@PreUpdate自動將孩子添加到父當孩子堅持,但在情況下,當我們在2級不同的線程2個的實體管理器(如在JBoss中),發行仍然存在,直到我們致電em.refresh(parent)

所以問題是 - 是否有辦法平穩消除問題並確保parent.getChildren()始終返回最新的兒童列表?

回答

8

大多數ORM的行爲都是這樣。

緩存中的對象未從數據庫更新(不需要額外讀取)。也可以將對象模型和持久性看作是分開的。即保持您的對象模型與自身一致,並且不要依賴持久性機制來爲您執行此操作。

所以,如果你想要將對象添加到集合中,那麼在「setParent」代碼中執行該操作。

這種情況下的最佳做法實際上是讓關係的一方完成所有工作,並讓另一方順從它。此外,我會建議使用字段訪問而不是方法訪問​​,這樣您可以更靈活地自定義方法。

添加到母公司的方法叫的addChild

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

,然後作出的setParent兒童:

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

setParent0兒童是兒童家長的財產stter。

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

我也建議了「的getChildren」方法返回一個不可變的集合,使開發人員不必無意中不要使用此方法(我學到的這一切困難的方式)。

還有一件事,你應該在上面的代碼中檢查代碼和其他防禦代碼的空值,爲了清楚起見,我把它放在了外面。

+0

找到謝謝您的回答廣泛,邁克爾。我發現了一些很好的信息。但是,恐怕它並不能解決我遇到的問題,因爲兩個不同的EntityManager實例擁有兩個不同的緩存,並且當其中一個更新實體實例時,另一個不會看到更新並且它的緩存實體變得過時 – artemb 2009-07-30 15:31:19

+0

聽起來就像你需要看更新觸發器,然後採取該對象並更新其他緩存。或者,如果您緩存解決方案suports羣集,則可以讓這兩個緩存成員位於同一個羣集中。 – 2009-07-30 15:46:16

+0

不幸的是,我無法控制Hibernate的會話緩存。或者我有嗎? – artemb 2009-07-31 07:21:27

1

關於緩存問題,當您有多個虛擬機針對具有單獨緩存的同一數據庫運行時,這是一個非常常見的問題。它被稱爲「緩存漂移」。

大多數對hibernate友好的緩存實現(ehcache,OSCache和SwarmCache)都有一個內置的分佈式緩存,可用於同步緩存。分佈式緩存通常會發送更新緩存狀態的多播消息。例如,通過SessionFactory.evict(Class,id)進行二級緩存驅逐將導致將無效消息發送到羣集中的其他緩存,這將使其他緩存中該對象的任何其他副本無效。

根據您的部署,多播可能會或可能不會被您接受。如果不是,您可能需要使用像Memcached這樣的單緩存解決方案。

我個人發現eh緩存的分佈式緩存配置非常簡單。

EH緩存討論在這裏稍微詳細的問題:http://ehcache.org/documentation/distributed_caching.html

4

很肯定你這裏的問題是您的級聯設置。

@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; 

    // ... 
} 

使用這些級聯設置將級聯持續和更新到子對象。

例如。在這個

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 

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

child.getParent(); // returns the parent 

更多信息,可以在Hibernate Annotations