如何在不加载父端 Set 集合的情况下持久化 Hibernate 子实体

Jan*_*fer 5 hibernate

我们使用 Hibernate 3.5.6-Final 和 Hazelcast 3.6.1 二级缓存。

情况

Parent我在 a和Child具有 Hibernate 设置的实体之间有双向、一对多的关系inverse = true。实体类定义如下:

class Parent {
   Set<Child> children;
   ... // setters, getters, other properties
}

class Child {
   Parent parent;
   ... // setters, getters, other properties       
}
Run Code Online (Sandbox Code Playgroud)

父级的 Hibernate 映射定义如下:

<set name="children"
     lazy="true"
     inverse="true"
     cascade="all"
     sort="unsorted">
     <cache usage="read-write"/>
     <key column="parent_id"/>
     <one-to-many class="Child"/>
</set>
Run Code Online (Sandbox Code Playgroud)

子进程的 Hibernate 映射定义如下:

<many-to-one name="parent"
    class="Parent"
    cascade="none"
    column="parent_id"
    not-null="true"/>
Run Code Online (Sandbox Code Playgroud)

当前的代码现在添加 aChild如下Parent

child.setParent(parent);
parent.getChildren().add(child);
Run Code Online (Sandbox Code Playgroud)

问题是第二行代码导致 Hibernate 加载所有子项。在我们的设置中,这是一个非常昂贵的操作,因为子实体具有急切集合,而子实体又具有急切集合的实体。

目前无法选择更改 Hibernate 模型。

问题

我将上面的代码修改如下:

child.setParent(parent);
sessionFactory.getCache().evictCollection( "Parent.children", parent.getId() );
Run Code Online (Sandbox Code Playgroud)

到目前为止这有效。现在明显的问题是,如果在执行代码之前加载children当前会话中的父级集合可能会过时。我想确保之后任何调用都会parent.getChildren()返回最新的集合,而无需实际将子项显式添加到集合中。我实际上想告诉 Hibernate 使该集合无效,以便它在需要时再次加载该集合。

Vla*_*cea 5

有一个更好的方法:

  1. 你需要让多对一的一方变得懒惰。

    <many-to-one name="parent"
        class="Parent"
        cascade="none"
        column="parent_id"
        not-null="true"
        lazy="true"/>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 只要您不需要在当前运行的 Persistence Context 中获取 Parent 实体,您就可以只获取 Proxy 引用:

    Parent parentProxy = session.load(Parent.class, parentId);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在,您可以简单地创建一个新的子项,如下所示:

    Child newChild = new Child();
    child.setParent(parentProxy);
    session.persist(newChild);
    
    Run Code Online (Sandbox Code Playgroud)

另一个解决方法如下:

您甚至不获取父代理引用,而是执行以下操作:

Parent parentReference = new Parent();
parentReference.setId(parentId);

Child newChild = new Child();
child.setParent(parentReference);
session.persist(newChild);
Run Code Online (Sandbox Code Playgroud)

这样,如果您只需要保留子实体,则无需完全获取父实体。


Jan*_*fer 0

我找到了解决问题的方法。我通过添加一个新方法解决了addChild这个问题,Parent如下所示:

 public void addChild(Child child) {
     child.setParent(this);
     if (Hibernate.isInitialized(getChildren()) {
         getChildren().add(child);
     } else {
         Hibernate.getSessionFactory().getCache().evictCollection(
             getClass().getName()+".children", this.getId());
     }
 }
Run Code Online (Sandbox Code Playgroud)

因此:如果由于某种原因已经加载了子项,则新的子项将添加到子项集中。这确保了已加载的集保持一致。如果该集合尚未加载,我只需将父级设置为子级并逐出二级缓存(此处描述了所需的操作: https: //github.com/hibernate/hibernate-orm/pull/580)。由于该集合尚未加载,因此也不会不一致。当之后访问该集合时,Hibernate 将加载该集合,包括新的子集合。