Hibernate@OneToMany与mappedBy(父子)关系和缓存问题

Hibernate@OneToMany与mappedBy(父子)关系和缓存问题,hibernate,caching,persistence,parent-child,one-to-many,Hibernate,Caching,Persistence,Parent Child,One To Many,我有这个问题已经有很长一段时间了,我已经在网上搜索了很多次,但还没有找到解决办法。我希望你能在这方面帮助我 我在以下两个实体之间存在父子关系: @Entity public class Parent { // ... @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) private Set<Child> children = new Ha

我有这个问题已经有很长一段时间了,我已经在网上搜索了很多次,但还没有找到解决办法。我希望你能在这方面帮助我

我在以下两个实体之间存在父子关系:

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

    // ...
}
我曾尝试使用@PreUpdate在子线程被持久化时自动将子线程添加到父线程中,但如果我们在两个不同的线程中有两个实体管理器(如JBoss),则问题仍然存在,直到调用
em.refresh(parent)


因此,问题是-是否有一种方法可以顺利地消除该问题,并确保
parent.getChildren()
始终返回最新的子项列表?

大多数ORM都会这样做

缓存中的对象不会从数据库中更新(不需要额外读取)。还可以将对象模型和持久性看作是分开的。i、 e.保持对象模型与自身一致,不要依赖持久性机制来为您做到这一点

因此,如果希望将对象添加到集合中,请在“setParent”代码中执行此操作

在这种情况下,最好的做法实际上是让关系的一方做所有的工作,让另一方服从。我还建议使用字段访问而不是方法访问,这样您可以更灵活地定制方法

将名为addChild的方法添加到父级

 public void addChild(Child child) {
    child.setParent0(this);
    getChildren().add(individualNeed);
 }
然后在子对象中设置setParent:

public void setParent(Parent parent) {
   parent.addChild(child);
}
setParent0 in Child是父对象在子对象上的属性stter

public void setParent0(Parent parent) {
   this.parent = parent;
}
我还建议“getChildren”方法返回一个不可变的集合,这样开发人员就不会无意中不使用这个方法(我从所有这些中学到了很多)


还有一件事,您应该在上面的代码中有空检查代码和其他防御代码,为了清楚起见,我省略了它。

关于缓存问题,当您有多个虚拟机针对具有单独缓存的同一数据库运行时,这是一个非常常见的问题。它被称为“缓存漂移”

大多数hibernate友好的缓存实现(ehcache、OSCache和SwarmCache)都内置了分布式缓存,可用于同步缓存。分布式缓存通常发送更新缓存状态的多播消息。例如,SessionFactory.Receive(Class,id)执行二级缓存逐出将导致无效消息发送到群集中的其他缓存,这将使该对象在其他缓存中的任何其他副本无效

根据您的部署,您可能会接受或不接受多播。如果不是,您可能需要使用memcached这样的单缓存解决方案

我个人发现eh cache的分布式缓存的配置非常简单


EH cache在这里更详细地讨论了这个问题:

非常确定您在这里的问题是您的级联设置

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

    // ...
}


有关这方面的更多信息,请访问

谢谢您的详细回答,Michael。我在里面找到了一些好信息。但是,恐怕这并不能解决我的问题,因为两个不同的EntityManager实例拥有两个不同的缓存,当其中一个更新实体实例时,另一个看不到更新,它的缓存实体变为过期的声音,就像您需要查看更新触发器一样,更新触发器将获取该对象并更新另一个缓存。或者,如果缓存解决方案支持集群,则可以使这两个缓存成为同一集群的成员。不幸的是,我无法控制Hibernate的会话缓存。还是我?
@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