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

发布于 2024-07-29 13:03:27 字数 917 浏览 6 评论 0原文

这个问题困扰我很久了,在网上查了很多资料,也没有找到解决办法。 我希望你能在这方面帮助我。

我在两个实体之间有如下所示的父子关系:

@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 中),问题仍然存在,直到我们调用 < code>em.refresh(parent)

所以问题是 - 有没有办法顺利消除问题并确保 parent.getChildren() 始终返回最新的列表孩子的?

I have this problem for a long time now, I have searched the web and SO in and out and didn't find a solution yet. I hope you can help me on that.

I have a parent-child relationship between two entities like the following:

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

    // ...
}

The thing is that when I create a new child and assign it to a parent, the parent doesn't get updated when it is in the cache already.

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

 // ...

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

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

I have tried to use @PreUpdate to automatically add the child to the parent when the child is persisted, but in the case when we have 2 entity managers in 2 different threads (like in JBoss), the issue still exists, until we call em.refresh(parent)

So the question is - is there a way to smoothly eliminate the problem and ensure that parent.getChildren() always return the up-to-date list of children?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦境 2024-08-05 13:03:27

大多数 ORM 都会这样做。

缓存中的对象不会从数据库更新(不必要的额外读取)。 还要将对象模型和持久性视为分开的。 即保持对象模型与其自身一致,并且不要依赖持久性机制来为您执行此操作。

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

在这种情况下,最佳做法实际上是让关系中的一方完成所有工作,并让另一方推迟完成。 另外,我建议使用字段访问而不是方法访问​​,这样您就可以更灵活地自定义方法。

在parent中添加一个名为addChild的方法

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

,然后在Child中设置Parent:

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

Child中的setParent0是parent对child的属性stter。

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

我还建议“getChildren”方法返回一个不可变的集合,以便开发人员不会无意中不使用此方法(我在这一切中学到了惨痛的教训)。

另一件事,上面的代码中应该有空检查代码和其他防御部分,为了清楚起见,我将其省略。

Most ORM's will behave this way.

The object in the cache is not updated from the database (an extra read that is not necessary). Also think of the object model and the persistence as separate. i.e. keep your object model consistent with itself and don't rely on the persistence mechanism to do this for you.

So if you want the object to be added to the collection then do that in the "setParent" code.

The best practice in this case is in fact to make one side of the relationship do all the work and let the other side defer onto it. Also I would suggest using field access rather than method access, that way you can customise methods with greater flexibility.

Add a method to parent called addChild

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

and then make setParent in Child:

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

setParent0 in Child is the property stter for parent on child.

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

I would also suggest that the "getChildren" method return an immutable collection so that developers don't inadvertantly not use this method (I learnt the hard way in all of this).

One more thing, you should have null checking code and other defensive pieces in the above code, I left it out for clarity.

怪我太投入 2024-08-05 13:03:27

很确定你的问题是你的级联设置。

@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 注解

Pretty sure your problem here is your Cascade settings.

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

    // ...
}

Using these cascade settings will cascade persist and updates to child objects.

eg.

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

or

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

child.getParent(); // returns the parent

more info on this can be found at Hibernate Annotations

遗弃M 2024-08-05 13:03:27

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

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

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

我个人觉得ehcache的分布式缓存的配置非常简单。

EH 缓存在这里更详细地讨论了这个问题: http://ehcache.org/documentation/distributed_caching.html

Regarding your issue with caching, this is a very common problem when you have multiple VMs running against the same database with separate caches. It's called "cache drift".

Most hibernate-friendly cache implementations (ehcache, OSCache and SwarmCache) have a distributed cache built-in that can be used to synchronize the caches. The distributed cache, generally, sends multicast messages updating the state of the cache. Doing a second level cache eviction by SessionFactory.evict(Class,id), for example, will cause an invalidation message to be sent to the other caches in the cluster which will invalidate any other copies of that object in other caches.

Depending on your deployment, the multicast may or may not be acceptable to you. If it is not you may need to use a single-cache solution like memcached.

I personally found the configuration of eh cache's distributed cache very straightforward.

EH cache discusses the problem in a bit more detail here: http://ehcache.org/documentation/distributed_caching.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文