Hibernate 创建新实体与修改现有实体时persist()的级联行为

Hibernate 创建新实体与修改现有实体时persist()的级联行为,hibernate,jpa,eclipselink,hibernate-cascade,Hibernate,Jpa,Eclipselink,Hibernate Cascade,我试图理解为什么persist()的行为在许多情况下是不同的,第一,持久化一个新实体,第二,修改那个实体。 我的测试设置有一名员工与部门进行单向多通;部门与员工之间没有关系。 对于测试,我在Employee中的Department字段上没有任何级联注释 我发现,在创建员工时,我必须调用em.persist(dept),否则dept实例不会持久化,我会得到一个异常。 因此,我调用em.persist(dept),以便持久化dept实体。 我的下一个测试是提交并启动新事务,使用em.find()检索

我试图理解为什么persist()的行为在许多情况下是不同的,第一,持久化一个新实体,第二,修改那个实体。 我的测试设置有一名员工与部门进行单向多通;部门与员工之间没有关系。 对于测试,我在Employee中的Department字段上没有任何级联注释

我发现,在创建员工时,我必须调用em.persist(dept),否则dept实例不会持久化,我会得到一个异常。 因此,我调用em.persist(dept),以便持久化dept实体。 我的下一个测试是提交并启动新事务,使用em.find()检索员工实体,修改部门名称,然后持久化员工。 我发现对dept的更改是持久的,尽管Employee中的depart字段没有任何级联注释

为什么会这样?为什么对部门的更改(通过em.persist(emp))被持久化到db,而没有对部门进行任何级联,但在员工首次持久化时,部门的创建不会持久化?我错过了什么? 顺便说一句,在考试结束时,对系名(进一步数学)的更改将持续。 谢谢

EDIT我刚刚读到“您可以在已经持久化的实例上调用此方法(persist()),但什么也不会发生”。我想这意味着在changeDept()中,我在find()之后对persist()的调用是多余的,所以我删除了它,结果是一样的。因此,这让我想到,除了许多误解之外,其中之一可能是我对persist()的理解,以及它与将实体(及其相关实体)的状态更改传播到db的关系。但是,Department上仍然没有级联类型的注释

EDIT2我想我有进展了。我添加了一个新方法,它创建一个新部门(“英语”),像以前一样使用find()检索员工,将员工的部门设置为新部门,并提交。我(谢天谢地,正如预期的那样)得到了一个例外:

java.lang.IllegalStateException:org.hibernate.TransientPropertyValueException:对象引用未保存的临时实例-在刷新之前保存临时实例

如果将PERSIST cascadeType放在Department字段中,则不会发生此异常。所以显而易见的是,persist适用于新实体的持久化;它不适用于将更改传播到现有实体。 问题仍然是,将更改传播到相关实体是否是默认行为(即未指定任何级联类型)?我想一定是这样

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

级联基本上指定在对当前实体执行操作时对基础实体执行的操作

在您的情况下,由于您没有在
员工
实体中的
部门
对象上指定任何级联类型,因此默认情况下,它会设置为
(默认级联类型,即在对员工对象执行操作时,不会对部门对象执行任何操作)。但是,由于您指定了员工和部门之间的关系,如果与该员工关联的部门尚未出现在数据库中,则您将面临
IllegalStateException
。这就是为什么需要分别
持久化
部门对象的原因


在第二种情况下,当您将级联类型提到
PERSIST
时,它会自动将
部门
Employee
对象一起持久化。

因此,您的问题归结为:为什么从数据库加载员工(及其部门)时会保存对员工部门的更改?因为JPA就是这样设计的:加载实体可以让您管理实体。它们的状态由JPA自动管理,因此对托管实体所做的任何更改都将透明地保存到数据库中。是的,谢谢。对于现有相关实体,当相关实体的状态更改时,将与父实体一起保存。但实体中不存在的相关实体不会被保存(默认情况下,即没有级联)。这是众多案例中的一个。我想当您知道它时,这是很明显的,但这是一个特例-它涉及到相关实体状态的传播,不需要任何级联来工作。类似地,我后来发现,如果合并一个包含另一个新实体的现有实体,则该相关实体是部分持久化的,没有级联。