Java 更新分离的对象时,Hibernate是否可以删除孤立的集合?

Java 更新分离的对象时,Hibernate是否可以删除孤立的集合?,java,hibernate,hibernate-cascade,Java,Hibernate,Hibernate Cascade,我知道删除孤立子对象是一个常见的问题,对于刚进入Hibernate的人来说也是一个常见的问题,而且相当标准的答案是确保您在子集合上有一些变体cascade=all、delete orphan或cascade=all delete orphan 我希望Hibernate能够检测到父对象中的子集合已清空/删除,并在更新父对象时从数据库中删除子表中的行。例如: Parent parent = session.get(...); parent.getChildren().clear(); session

我知道删除孤立子对象是一个常见的问题,对于刚进入Hibernate的人来说也是一个常见的问题,而且相当标准的答案是确保您在子集合上有一些变体
cascade=all、delete orphan
cascade=all delete orphan

我希望Hibernate能够检测到父对象中的子集合已清空/删除,并在更新父对象时从数据库中删除子表中的行。例如:

Parent parent = session.get(...);
parent.getChildren().clear();
session.update(parent);
父类的当前映射如下所示:

<bag name="children" cascade="all-delete-orphan">
    <key column="parent_id" foreign-key="fk_parent_id"/>
    <one-to-many class="Child"/>
</bag>
当传递到
会话更新(父)
时,Hibernate是否可以在分离的父对象中检测孤立的子集合?还是我以某种方式错误地使用了分离的对象


我的希望是,我可以避免与Hibernate进行任何复杂的交互,以将更改持久化到分离的实例。调用
session.update(parent)
后,我的API方法无需进一步修改分离的对象,此方法仅负责持久化远程客户端应用程序所做的更改。

我认为,在使用分离的会话时,您可能会面临集合问题。我建议您首先使用集合加载实体,然后使用更改更新该实体,这将有所帮助。

您的映射(简化)

照你说的

我希望能够让Hibernate检测到子集合已从父对象中删除,并在更新父对象时从数据库中删除子表中的

public class WhatYouWantTest {

    private static SessionFactory sessionFactory;

    private Serializable parentId;

    private Serializable firstId;
    private Serializable secondId;

    @BeforeClass
    public static void setUpClass() {
        Configuration c = new Configuration();
        c.addResource("mapping.hbm.3988215.xml");

        sessionFactory = c.configure().buildSessionFactory();
    }

    @Before
    public void setUp() throws Exception {
        Parent parent = new Parent();
        Child first   = new Child();
        Child second  = new Child();

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        parentId = session.save(parent);
        firstId  = session.save(first);
        secondId = session.save(second);

        parent.getChildList().add(first);
        parent.getChildList().add(second);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    public void removed_second_from_parent_remove_second_from_database() {
        Parent parent = new Parent();
        parent.setId((Integer) parentId);

        Child first = new Child();
        first.setId((Integer) firstId);

        /**
          * It simulates the second one has been removed
          */
        parent.getChildList().add(first);

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        session.update(parent);

        session.getTransaction().commit();
        session.close();

        session = sessionFactory.openSession();
        session.beginTransaction();

        Child second = (Child) session.get(Child.class, secondId);
        Assert.assertNull(second);

        session.getTransaction().commit();
        session.close();
    }
}
差不多

Parent parent = session.get(...);
parent.getChildren().clear();

session.update(parent);
您说它可以正常工作,因为您有一个附加的父实例

现在让我们看看下面的一个(注意Assert.assertNull(第二个)

不幸的是,考试没有通过。你能做什么

  • 启用长时间运行的对话
Hibernate参考说

扩展(或长)会话—在提交数据库事务后,Hibernate会话可能会断开与底层JDBC连接的连接,并在出现新的客户端请求时重新连接。这种模式被称为每次会话,甚至不需要重新连接。自动版本控制用于隔离并发修改,通常不允许自动刷新会话,而是明确地刷新会话

免责声明:我没有任何使用长时间对话的场景。JavaEE有状态会话bean支持长时间运行的会话。但是它支持JPA(不是Hibernate)

您可以创建一个替代映射,使您的子元素成为复合元素。因为它的生命周期取决于父对象,所以您可以依赖复合元素来获得所需的内容

创建一个名为AlternativeParent的类,该类扩展Parent

public class AlternativeParent extends Parent {}
现在是它的映射(注意子元素是复合元素,而不是普通的@Entity)

如果我重构上面显示的测试用例(现在改用AlternativeParent)


我看到一个绿色条

你的意思是,加载现有实体,然后用传递给我的分离实例API加载它?@mattb:你可以编写一个逻辑来合并集合,甚至只是替换以前的集合并设置新的集合,但要确保在hbm中添加了标记。这将包含一个简单的sql delete查询,用于在保存新集合之前删除整个集合。或者以其他方式,无论何时添加任何内容,每次都会触发此sql删除,然后添加集合的新副本。这可以解决每次手动更新集合的问题。不确定我是否喜欢此解决方案,因为它需要更改我的使用模式-例如添加
。希望这可以通过单独的映射来解决。
Parent parent = session.get(...);
parent.getChildren().clear();

session.update(parent);
public class WhatYouWantTest {

    private static SessionFactory sessionFactory;

    private Serializable parentId;

    private Serializable firstId;
    private Serializable secondId;

    @BeforeClass
    public static void setUpClass() {
        Configuration c = new Configuration();
        c.addResource("mapping.hbm.3988215.xml");

        sessionFactory = c.configure().buildSessionFactory();
    }

    @Before
    public void setUp() throws Exception {
        Parent parent = new Parent();
        Child first   = new Child();
        Child second  = new Child();

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        parentId = session.save(parent);
        firstId  = session.save(first);
        secondId = session.save(second);

        parent.getChildList().add(first);
        parent.getChildList().add(second);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    public void removed_second_from_parent_remove_second_from_database() {
        Parent parent = new Parent();
        parent.setId((Integer) parentId);

        Child first = new Child();
        first.setId((Integer) firstId);

        /**
          * It simulates the second one has been removed
          */
        parent.getChildList().add(first);

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        session.update(parent);

        session.getTransaction().commit();
        session.close();

        session = sessionFactory.openSession();
        session.beginTransaction();

        Child second = (Child) session.get(Child.class, secondId);
        Assert.assertNull(second);

        session.getTransaction().commit();
        session.close();
    }
}
public class AlternativeParent extends Parent {}
<class name="AlternativeParent" table="PARENT">
    <id name="id">
        <generator class="native"/>
    </id>
    <bag name="childList" table="CHILD">
        <key column="PARENT_ID" not-null="false"/>
        <composite-element class="Child">
            <property column="CHILD_ID" name="id"/>
        </composite-element>
    </bag>
</class>
public boolean equals(Object o) {
    if (!(o instanceof Child))
        return false;

    Child other = (Child) o;
    // identity equality
    // Used by composite elements
    if(getId() != null) {
        return new EqualsBuilder()
                   .append(getId(), other.getId())
                   .isEquals();
    } else {
        // object equality
     }
}
@Test
public void removed_second_from_parent_remove_second_from_database() {
    AlternativeParent parent = new AlternativeParent();
    parent.setId((Integer) parentId);

    Child first = new Child();
    first.setId((Integer) firstId);

    /**
      * It simulates the second one has been removed
      */
    parent.getChildList().add(first);

    Session session = sessionFactory.openSession();
    session.beginTransaction();

    session.update(parent);

    session.getTransaction().commit();
    session.close();

    session = sessionFactory.openSession();
    session.beginTransaction();

    Child second = (Child) session.get(Child.class, secondId);
    Assert.assertNull(second);

    session.getTransaction().commit();
    session.close();

}