创建新实体与修改现有实体时 persist() 的级联行为
Cascade behaviour for persist() when creating a new entity vs modifying an existing entity
我试图理解为什么在 ManyToOne 上 persist() 的行为在第一种情况下是不同的,持久化一个新实体,第二种情况是修改该实体。
我的测试设置有 Employee 有一个单向的 ManyToOne 和 Department;没有从 Department 到 Employee 的关系。
对于测试,我在 Employee 的 Department 字段上没有任何级联注释。
我发现在创建 Employee 时,我必须调用 em.persist(dept) 否则 dept 实例不会持久化,我会得到一个异常。
因此,我正在调用 em.persist(dept) 以便保留 dept 实体。
我的下一个测试是提交并开始一个新事务,使用 em.find() 检索员工实体,修改 dept.name,然后保留员工。
我发现的是,尽管在 Employee 的 Department 字段上没有任何级联注释,但对 dept 的更改仍然存在。
这是为什么?为什么对部门的更改会(通过 em.persist(emp))持久保存到数据库,而部门上没有任何级联,但是当员工第一次持久保存时,部门的创建不会持久保存?我错过了什么?
顺便说一句,在测试结束时,对部门名称(FURTHER MATHS)的更改仍然存在。
谢谢。
编辑 我刚刚读到“您可以在已经持久化的实例上调用此方法 (persist()),但没有任何反应" 在 https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate 。我认为这意味着在 changeDept() 中我在 find() 之后对 persist() 的调用是多余的,所以我删除了它并且结果是一样的。所以这让我想到除了许多误解之外,其中之一可能是我对 persist() 的理解,以及它如何与将实体(及其相关实体)的状态变化传播到数据库有关或无关。但是,Department 上仍然没有 cascadeType 注释。
EDIT2 我想我有所进展。我添加了一个新方法,它创建一个新部门 ("ENGLISH"),像以前一样使用 find() 检索员工,将员工的部门设置为新部门,然后提交。我得到(谢天谢地,正如预期的那样)一个例外:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
如果我将 PERSIST cascadeType 放在 Department 字段上,则不会发生此异常。所以很明显,persist 适用于新实体的持久化;它不适用于将更改传播到现有实体。
问题仍然存在,将更改传播到相关实体是默认行为(即没有指定任何 cascadeType)吗?我想它一定是。
@Entity
public class Employee {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
private double salary;
@ManyToOne
private Department department;
...
}
部门:
@Entity
public class Department {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
..
}
方法:
static void setupData() {
try {
em.getTransaction().begin();
Department dept = new Department();
dept.setName("MATHS");
...
Employee emp2 = new Employee("Darth Vader", 10003, dept);
em.persist(emp2);
em.persist(dept); //needed without cascadeType=PERSIST on Department
em.getTransaction().commit();
} catch (Exception e) {
logger.error("oops: " + e);
e.printStackTrace();
em.getTransaction().rollback();
}
}
static void changeDept() {
try {
em.clear();
em.getTransaction().begin();
Employee emp1 = em.find(Employee.class, 2);
logger.info("emp1:" + emp1);
Department dept = emp1.getDepartment();
dept.setName("FURTHER MATHS");
em.persist(emp1);
em.getTransaction().commit();
} catch (Exception e) {
logger.error("oops: " + e);
e.printStackTrace();
em.getTransaction().rollback();
}
}
级联基本上指定了在对当前实体执行操作时要对基础实体执行的操作。
在您的情况下,由于您没有在 Employee
实体中的 department
对象上指定任何级联类型,默认情况下它会设置为 NONE
(默认级联类型,也就是说,当您对 Employee 对象执行操作时,不会对 Department 对象采取任何操作)。但是由于您指定了 Employee 和 Department 之间的关系,如果与 Employee 关联的部门尚未存在于数据库中,那么您将面临 IllegalStateException
。这就是为什么您需要单独 persist
Department 对象。
在第二种情况下,当你提到级联类型PERSIST
时,它会自动将department
与Employee
对象一起持久化。
我试图理解为什么在 ManyToOne 上 persist() 的行为在第一种情况下是不同的,持久化一个新实体,第二种情况是修改该实体。 我的测试设置有 Employee 有一个单向的 ManyToOne 和 Department;没有从 Department 到 Employee 的关系。 对于测试,我在 Employee 的 Department 字段上没有任何级联注释。
我发现在创建 Employee 时,我必须调用 em.persist(dept) 否则 dept 实例不会持久化,我会得到一个异常。 因此,我正在调用 em.persist(dept) 以便保留 dept 实体。 我的下一个测试是提交并开始一个新事务,使用 em.find() 检索员工实体,修改 dept.name,然后保留员工。 我发现的是,尽管在 Employee 的 Department 字段上没有任何级联注释,但对 dept 的更改仍然存在。
这是为什么?为什么对部门的更改会(通过 em.persist(emp))持久保存到数据库,而部门上没有任何级联,但是当员工第一次持久保存时,部门的创建不会持久保存?我错过了什么? 顺便说一句,在测试结束时,对部门名称(FURTHER MATHS)的更改仍然存在。 谢谢。
编辑 我刚刚读到“您可以在已经持久化的实例上调用此方法 (persist()),但没有任何反应" 在 https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate 。我认为这意味着在 changeDept() 中我在 find() 之后对 persist() 的调用是多余的,所以我删除了它并且结果是一样的。所以这让我想到除了许多误解之外,其中之一可能是我对 persist() 的理解,以及它如何与将实体(及其相关实体)的状态变化传播到数据库有关或无关。但是,Department 上仍然没有 cascadeType 注释。
EDIT2 我想我有所进展。我添加了一个新方法,它创建一个新部门 ("ENGLISH"),像以前一样使用 find() 检索员工,将员工的部门设置为新部门,然后提交。我得到(谢天谢地,正如预期的那样)一个例外:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
如果我将 PERSIST cascadeType 放在 Department 字段上,则不会发生此异常。所以很明显,persist 适用于新实体的持久化;它不适用于将更改传播到现有实体。 问题仍然存在,将更改传播到相关实体是默认行为(即没有指定任何 cascadeType)吗?我想它一定是。
@Entity
public class Employee {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
private double salary;
@ManyToOne
private Department department;
...
}
部门:
@Entity
public class Department {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
..
}
方法:
static void setupData() {
try {
em.getTransaction().begin();
Department dept = new Department();
dept.setName("MATHS");
...
Employee emp2 = new Employee("Darth Vader", 10003, dept);
em.persist(emp2);
em.persist(dept); //needed without cascadeType=PERSIST on Department
em.getTransaction().commit();
} catch (Exception e) {
logger.error("oops: " + e);
e.printStackTrace();
em.getTransaction().rollback();
}
}
static void changeDept() {
try {
em.clear();
em.getTransaction().begin();
Employee emp1 = em.find(Employee.class, 2);
logger.info("emp1:" + emp1);
Department dept = emp1.getDepartment();
dept.setName("FURTHER MATHS");
em.persist(emp1);
em.getTransaction().commit();
} catch (Exception e) {
logger.error("oops: " + e);
e.printStackTrace();
em.getTransaction().rollback();
}
}
级联基本上指定了在对当前实体执行操作时要对基础实体执行的操作。
在您的情况下,由于您没有在 Employee
实体中的 department
对象上指定任何级联类型,默认情况下它会设置为 NONE
(默认级联类型,也就是说,当您对 Employee 对象执行操作时,不会对 Department 对象采取任何操作)。但是由于您指定了 Employee 和 Department 之间的关系,如果与 Employee 关联的部门尚未存在于数据库中,那么您将面临 IllegalStateException
。这就是为什么您需要单独 persist
Department 对象。
在第二种情况下,当你提到级联类型PERSIST
时,它会自动将department
与Employee
对象一起持久化。