在更新分离的对象时,Hibernate可以删除孤立的集合吗?

我知道删除孤立的子对象是一个常见的问题,对于刚接触Hibernate的人来说是一个常见的问题,而且相当标准的答案是确保你有一些变化的cascade=all,delete-orphancascade=all-delete-orphan上的cascade=all-delete-orphan

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

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

Parent类的当前映射如下所示:

     

在更新附加对象时,这对我来说很好,但是我有一个用例,我们希望能够获取一个分离的对象(由远程客户端通过HTTP / JSON发送到我们的API方法),并将其直接传递给Hibernate会话 – 允许客户端能够以他们喜欢的方式操作父对象并保持更改。

在我的分离对象上调用session.update(parent)时,子表中的行是孤立的(FK列设置为null)但未删除。 请注意,当我调用session.update() ,这是Hibernate Session第一次看到此对象实例 – 我没有以任何其他方式重新附加或合并对象与Session。 我依靠客户端传递其标识符对应于数据库中实际对象的对象。 例如,我的API服务方法中的逻辑是这样的:

 String jsonString = request.getParameter(...); Parent parent = deserialize(jsonString); session.update(parent); 

当传递给session.update(parent)时,Hibernate是否有可能在分离的父对象中检测孤立的session.update(parent) ? 或者我是否以某种方式错误地使用了分离的对象?

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

你的映射(简化)

                   

产生

 PARENT ID CHILD CHILD_ID PARENT_ID 

根据你说的

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

就像是

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

你说它工作正常, 因为你有一个附加的父实例

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

 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(); } } 

不幸的是 ,测试没有通过。 你可以做什么 ???

  • 启用长时间对话

Hibernate参考说

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

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

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

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

 public class AlternativeParent extends Parent {} 

现在它的映射(注意Child作为复合元素而不是普通的@Entity)

            

现在在Child类中实现一个方便的equals方法

 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 } } 

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

 @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(); } 

我看到一个绿色的酒吧

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