【问题标题】:What is the correct way to use CascadeType.MERGE with Spring Data Repositories?将 CascadeType.MERGE 与 Spring Data Repositories 一起使用的正确方法是什么?
【发布时间】:2018-03-14 05:30:28
【问题描述】:

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

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

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);

【问题讨论】:

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


    【解决方案1】:

    简答

    它会正常工作(只产生一条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);
        }
    }
    

    此外,如您所见,您不必担心持久/合并调用 - 如果它是 new 实体,将调用 persist()isNew() 检查很简单(见AbstractPersistable 源代码):

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

    没有@Transactional

    将有两个事务 - 每个save() 调用一个。

    • entity1entity2entity3 现在是瞬态
    • 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 现在是瞬态
    • 事务已打开,会话已创建
      • Entity2Repository.save(entity2) 被调用
      • persist()entity2 调用,因为它是新实体
      • persist() 级联到 entity1
      • entity1entity2 现在是PERSISTENT,它们已附加到当前会话
      • Entity3Repository.save(entity3) 被调用
      • persist()entity3 调用,因为它是新实体
      • persist() 级联到 entity1,在一级缓存中找到托管实体
      • entity3 现在是PERSISTENT,它附加到当前会话
    • 事务已提交,会话已关闭

    entity1entity2entity3 条目保存在数据库中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-10
      • 2013-01-02
      • 1970-01-01
      • 2020-08-27
      • 2020-07-10
      • 2020-09-29
      • 1970-01-01
      • 2012-05-12
      相关资源
      最近更新 更多