当前位置: 首页 > 知识库问答 >
问题:

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

姬自强
2023-03-14

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

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

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

编辑我刚刚读到“您可以在已经持久化的实例上调用此方法(persist()),但什么都不会发生”https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate。我想这意味着在changeDept()中,我对find()后persist()的调用是多余的,所以我删除了它,结果是一样的。因此,这让我想到,除了许多误解之外,其中之一可能是我对persist()的理解,以及它与将实体(及其相关实体)的状态变化传播给db的关系。但仍然没有关于部门的级联类型注释。

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

<代码>java。lang.IllegalStateException:组织。冬眠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();
        } 
    }

共有1个答案

全飞扬
2023-03-14

级联基本上指定了在当前实体上执行操作时要对底层实体执行的操作。

在您的情况下,由于您没有在员工实体中的部门对象上指定任何级联类型,因此默认情况下,它会设置为无(默认级联类型,即在对员工对象执行操作时,不会对部门对象执行任何操作)。但由于您指定了员工和部门之间的关系,如果与该员工关联的部门在数据库中尚不存在,则您将面临非法状态异常。这就是为什么需要单独保存Department对象的原因。

在第二种情况下,当您将级联类型提到持久化时,它将自动持久化部门和员工对象。

 类似资料:
  • 我有一个表,其中包含。 我还有一个硬编码表,其中我已经为每个国家/地区创建了一个区域,国家/地区是主键。 我想加入

  • 假设我在前面有一个表单,它有常用的字段和下拉列表。在这些下拉列表中,用户可以选择一个选项,并且每个选项都链接到Spring data JPA中的一个实体; 下拉列表包含一些标签和对应实体的链接作为值。然后,这个值在POST-request中传递给我们希望创建的实体的PagingAndSorting存储库。 假设它是一个具有username的用户,并且他必须与其中一个办公室(也是一个实体)关联: 我

  • 对于新学生和新课程的情况,它工作得很好,但是在新课程和现有学生的情况下,我会从数据库中抛出一个唯一的约束冲突。关系类似于下面。 在持久化之前,我尝试使用entitymanager查找一个学生,如果该学生存在,则使用该实例而不是新的学生实例。这也不起作用,它仍然尝试执行插入而不是更新

  • 如果我只传递一个字符串,而不是的列表: 我得到的是:

  • 我正在尝试为实体创建一个域模型(聚合),该实体包含子实体,并且可以有父实体。我们有实体包。我们可以把包裹分成更小的包裹(儿童)。每个分开的包都有一个父包。域的哪个部分应该包含域逻辑:聚合还是域服务?e、 g.聚合包有一个方法 分包还是原包?解决这个问题的正确方法是什么? 我希望我的域模型被ddd(应用程序和基础设施)的其他部分保持干净和幂等

  • 我试图实现的是一个创建/编辑用户工具。可编辑字段有: null null 注意:findAllRolesExceptOwnedByUser()是一个自定义的存储库函数,返回所有角色(那些尚未分配给$User的角色)的子集。 1.3.1添加角色: 1.3.2删除角色: 由于组和角色有相同的问题,我在这里跳过它们。如果我让角色发挥作用,我知道我也能在团队中做到这一点。 没有必要发布这些内容,因为它们工