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

时间:2017-10-03 04:55:39

标签: java spring jpa spring-boot spring-data

this question提交保存entity2entity3实例的事务,每个实例都持有对Entity1(entity1)实例的引用,导致出现的entity1的两条记录数据库(因为保存每个实例时保存引用)。

如果我有2个Spring数据存储库 - Entity2RepositoryEntity3Repository,我将执行以下操作,这将导致数据库中包含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);

1 个答案:

答案 0 :(得分:5)

简短回答

它将正常工作(只生成entity1的一条记录。

答案很长

示例代码执行的实际结果取决于测试方法中@Transactional注释的存在。

Entity2Repository.save(entity2);
Entity3Repository.save(entity3);

这些调用将调用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);
    }
}

另外,如您所见,您不应该担心持久/合并调用 - 如果它是实体,则会调用persist()isNew()检查非常简单(请参阅AbstractPersistable源代码):

public boolean isNew() {
    return null == getId();
}

没有@Transactional

将有两个交易 - 每个save()来电一个。

  • entity1entity2entity3 TRANSIENT 现在
  • Entity2Repository.save(entity2)已被调用
  • 打开交易,创建会话
    • persist()被调用entity2,因为它是新实体
    • persist()级联为entity1
    • entity1entity2现在 PERSISTENT ,它们附在 本届会议
  • 提交交易,会话已关闭
  • entity1entity2 已脱离现在
  • Entity3Repository.save(entity3)已被调用
  • 打开交易,创建会话
    • persist()被调用entity3,因为它是新实体
    • persist()级联为entity1
    • org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist被抛出
  • 回滚交易,会话关闭

entity1entity2条目保存在数据库中。

使用@Transactional

默认@Transactional传播是REQUIRED - 只有一个交易。

  • entity1entity2entity3 TRANSIENT 现在
  • 打开交易,创建会话
    • Entity2Repository.save(entity2)已被调用
    • persist()被调用entity2,因为它是新实体
    • persist()级联为entity1
    • entity1entity2现在 PERSISTENT ,它们附加到当前会话
    • Entity3Repository.save(entity3)已被调用
    • persist()被调用entity3,因为它是新实体
    • persist()级联到entity1,在第一级缓存中找到的托管实体
    • entity3现在 PERSISTENT ,它附加到当前会话
  • 提交交易,会话已关闭

entity1entity2entity3条目保存在数据库中。