将CascadeType.MERGE与Spring Data Repositories一起使用的正确方法是什么?

Ole*_*Ole 4 java spring jpa spring-data spring-boot

这个问题的承诺,节省的情况下,交易entity2entity3,其各自持有至ENTITY1(使用实体)实例的引用,导致在数据库中出现的两个记录使用实体的(因为当每个实例都保存在引用保存) .

如果我有2个Spring数据存储库 - Entity2Repository并且Entity3Repository我执行以下操作将导致数据库中包含2个实体1实例:

Entity1 entity1 = new Entity1();
entity1.name = "Name1";
entity1.value = "Value1";

Entity2 entity2 = new Entity2();
entity2.name = "Name2";
entity2.value = "Value2";
entity2.setEntity1(entity1);

Entity3 entity3 = new Entity3();
entity3.name = "Name3";
entity3.value = "Value3";
entity3.setEntity1(entity1);

Entity2Repository.save(entity2);
Entity3Repository.save(entity3);
Run Code Online (Sandbox Code Playgroud)

Ana*_*mov 5

简短的回答

它将正常工作(只生成一个记录entity1).

答案很长

示例代码执行的实际结果取决于测试方法上的@Transactional注释.

Entity2Repository.save(entity2);
Entity3Repository.save(entity3);
Run Code Online (Sandbox Code Playgroud)

这些调用将调用SimpleJpaRepository#save()方法,即@Transactional本身.

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,如您所见,您不应该担心持久/合并调用 - 如果它是一个实体,persist()将被调用.isNew()检查非常简单(参见AbstractPersistable源代码):

public boolean isNew() {
    return null == getId();
}
Run Code Online (Sandbox Code Playgroud)

没有@Transactional

将有两个交易 - 每个交易一个save().

  • entity1,entity2并且entity3瞬态现在
  • Entity2Repository.save(entity2) 被调用
  • 打开事务,创建会话
    • persist()被调用entity2,因为它是新实体
    • persist() 是级联到 entity1
    • entity1entity2持久性现在,他们都附加到当前会话
  • 提交事务,会话关闭
  • entity1并且现在entity2已经被拆除了
  • Entity3Repository.save(entity3) 被调用
  • 打开事务,创建会话
    • persist()被调用entity3,因为它是新实体
    • persist() 是级联到 entity1
    • org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist 被扔了
  • 事务回滚,会话关闭

entity1并且entity2条目保存在数据库中.

使用@Transactional

默认@Transactional传播是REQUIRED- 只有一个事务.

  • entity1,entity2并且entity3瞬态现在
  • 打开事务,创建会话
    • Entity2Repository.save(entity2) 被调用
    • persist()被调用entity2,因为它是新实体
    • persist() 是级联到 entity1
    • entity1entity2持久性现在,他们都附加到当前会话
    • Entity3Repository.save(entity3) 被调用
    • persist()被调用entity3,因为它是新实体
    • persist()级联到entity1,在第一级缓存中找到的托管实体
    • entity3现在是PERSISTENT,它附属于本届会议
  • 提交事务,会话关闭

entity1,entity2entity3条目保存在数据库中.