2009-07-30 135 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