mat*_*t b 16 java hibernate hibernate-cascade
我知道,删除孤立的子对象是SO一个常见的问题以及人们对Hibernate一个共同的问题,那还算标准答案是,以确保你有一些变化cascade=all,delete-orphan或者cascade=all-delete-orphan对孩子的集合.
我希望能够让Hibernate检测到子集合已从父对象中清空/删除,并在更新父对象时从子数据库中删除子表中的行.例如:
Parent parent = session.get(...);
parent.getChildren().clear();
session.update(parent);
Run Code Online (Sandbox Code Playgroud)
我目前的Parent班级映射如下:
<bag name="children" cascade="all-delete-orphan">
<key column="parent_id" foreign-key="fk_parent_id"/>
<one-to-many class="Child"/>
</bag>
Run Code Online (Sandbox Code Playgroud)
在更新附加对象时,这对我来说很好,但是我有一个用例,我们希望能够获取一个分离的对象(由远程客户端通过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);
Run Code Online (Sandbox Code Playgroud)
是否有可能Hibernate在传递给分离的父对象时检测孤立的子集合session.update(parent)?或者我是否以某种方式错误地使用了分离的对象?
我希望我可以避免与Hibernate进行任何复杂的交互,以便将更改保存到分离的实例中.在调用之后,我的API方法无需进一步修改分离的对象session.update(parent),此方法仅负责持久保存远程客户端应用程序所做的更改.
你的映射(简化)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="br.com._3988215.model.domain">
<class name="Parent" table="PARENT">
<id name="id">
<generator class="native"/>
</id>
<bag cascade="all,delete-orphan" name="childList">
<key column="PARENT_ID" not-null="false"/>
<one-to-many class="Child"/>
</bag>
</class>
<class name="Child" table="CHILD">
<id name="id" column="CHILD_ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)
产生
PARENT
ID
CHILD
CHILD_ID
PARENT_ID
Run Code Online (Sandbox Code Playgroud)
根据你说的
我希望能够让Hibernate 检测到子集合已从父对象中删除,并在父对象更新时从子数据库中删除子表中的行
就像是
Parent parent = session.get(...);
parent.getChildren().clear();
session.update(parent);
Run Code Online (Sandbox Code Playgroud)
你说它工作正常,因为你有一个附加的父实例
现在让我们看一下(注意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();
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,测试没有通过.你可以做什么 ???
Hibernate参考说
扩展(或长)会话 - 在提交数据库事务后,Hibernate会话可能与基础JDBC连接断开连接,并在发生新客户端请求时重新连接.这种模式称为每次会话会话,甚至不需要重新附加.自动版本控制用于隔离并发修改,并且通常不允许自动刷新会话,而是明确地刷新会话.
免责声明:我没有任何使用长时间对话的场景.Java EE有状态会话bean支持长时间运行的会话.但它支持JPA(不是Hibernate)
或者,您可以创建一个替代映射,使您的Child成为复合元素.因为它的生命周期取决于父对象,所以您可以依靠复合元素来获得所需的内容
创建一个名为AlternativeParent的类,它扩展了Parent
public class AlternativeParent extends Parent {}
Run Code Online (Sandbox Code Playgroud)
现在它的映射(注意Child作为复合元素而不是普通的@Entity)
<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>
Run Code Online (Sandbox Code Playgroud)
现在在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
}
}
Run Code Online (Sandbox Code Playgroud)
如果我重构上面显示的测试用例(现在改为使用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();
}
Run Code Online (Sandbox Code Playgroud)
我看到一个绿色的酒吧
| 归档时间: |
|
| 查看次数: |
8534 次 |
| 最近记录: |