0

我试图理解为什么persist() 的行为在ManyToOne 上是不同的,首先是持久化一个新实体,其次是修改该实体。我的测试设置让 Employee 与 Department 有一个单向的 ManyToOne;部门与员工之间没有关系。对于测试,我在 Employee 的 Department 字段上没有任何级联注释。

我发现在创建 Employee 时,我必须调用 em.persist(dept) 否则 dept 实例不会被持久化并且我得到一个异常。所以,我正在调用 em.persist(dept) 以便保留 dept 实体。我的下一个测试是提交并开始一个新事务,使用 em.find() 检索员工实体,修改 dept.name,然后持久化员工。我发现,尽管 Employee 的 Department 字段上没有任何级联注释,但对 dept 的更改仍然存在。

为什么是这样?为什么对部门的更改会持久化(通过 em.persist(emp))到数据库,而部门上没有任何级联,但是当员工第一次持久化时,部门的创建不会持久化?我错过了什么?顺便说一句,为了清楚起见,在测试结束时,对部门名称(进一步数学)的更改仍然存在。谢谢。

编辑我刚刚在https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate上读到“您可以在已经持久化的实例上调用此方法 (persist()),但什么也没有发生” 。我认为这意味着在 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 字段上,则不会发生此异常。所以显而易见的是,持久化适用于新实体的持久化;它不适用于将更改传播到现有实体。问题仍然存在,将更改传播到相关实体是否是默认行为(即没有指定任何 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();
        } 
    }
4

1 回答 1

1

如果对当前实体执行操作,级联基本上指定要在基础实体上执行的操作。

在您的情况下,由于您没有department在实体中的对象上指定任何级联类型Employee,默认情况下它被设置为NONE(默认级联类型,即当您对 Employee 对象执行操作时,不会对 Department 对象执行任何操作)。但是由于您指定了 Employee 和 Department 之间的关系,如果与 Employee 关联的部门尚未出现在数据库中,那么您将面临IllegalStateException。这就是为什么您需要单独persist的 Department 对象。

在第二种情况下,当你提到级联类型时PERSIST,它会自动departmentEmployeeObject 一起持久化。

于 2018-09-12T05:35:47.740 回答