为什么我们必须在 Data Jpa 中使用 @Modifying 注解进行查询

2023-12-05

例如,我的 CRUD 接口中有一个方法可以从数据库中删除用户:

public interface CrudUserRepository extends JpaRepository<User, Integer> {

    @Transactional
    @Modifying
    @Query("DELETE FROM User u WHERE u.id=:id")
    int delete(@Param("id") int id, @Param("userId") int userId);
}

此方法仅适用于注释@Modifying。但是这里需要注释什么呢?为什么Spring不能分析查询并理解它是一个修改查询?


CAUTION!

Using @Modifying(clearAutomatically=true)将删除持久化上下文中托管实体上的任何挂起的更新 spring 声明如下:

这样做会触发将该方法注释为更新的查询 查询而不是选择一个。由于 EntityManager 可能包含 执行修改查询后过时的实体,我们这样做 不会自动清除它(请参阅 EntityManager.clear() 的 JavaDoc 有关详细信息),因为这会有效地删除所有未刷新的更改 仍在 EntityManager 中等待处理。如果您希望 EntityManager 自动清除,可以设置@Modifying注解 清除自动属性为true。

幸运的是,从Spring Boot 2.0.4.RELEASE添加了 Spring 数据flushAutomatically flag (https://jira.spring.io/browse/DATAJPA-806) 自动刷新持久化上下文中的任何托管实体before执行修改查询检查参考https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/api/org/springframework/data/jpa/repository/Modifying.html#flushAutomatically

So the safest使用方法@Modifying is :

@Modifying(clearAutomatically=true, flushAutomatically=true)

如果我们不使用这两个标志会发生什么?

考虑以下代码:

repo {
   @Modifying
   @Query("delete User u where u.active=0")
   public void deleteInActiveUsers();

}

场景1 为什么flushAutomatically

 service {
        User johnUser = userRepo.findById(1); // store in first level cache
        johnUser.setActive(false);
        repo.save(johnUser);

        repo.deleteInActiveUsers();// BAM it won't delete JOHN right away
        
        // JOHN still exist since john with active being false was not 
        // flushed into the database when @Modifying kicks in
        // so imagine if after `deleteInActiveUsers` line you called a native 
        // query or started a new transaction, both cases john 
        // was not deleted so it can lead to faulty business logic 
    }

场景2 为什么clearAutomatically 在下面考虑 johnUser.active 已经是 false

service {
       User johnUser = userRepo.findById(1); // store in first level cache
       repo.deleteInActiveUsers(); // you think that john is deleted now 
       System.out.println(userRepo.findById(1).isPresent()) // TRUE!!!
       System.out.println(userRepo.count()) // 1 !!!
       
       // JOHN still exists since in this transaction persistence context
       // John's object was not cleared upon @Modifying query execution, 
       // John's object will still be fetched from 1st level cache 
       // `clearAutomatically` takes care of doing the 
       // clear part on the objects being modified for current 
       // transaction persistence context
}

因此,如果 - 在同一个事务中 - 您正在执行的行之前或之后修改了对象@Modifying,然后使用clearAutomatically & flushAutomatically如果没有,那么您可以跳过使用这些标志

顺便说一句,这是您应该始终将@Transactional服务层上的注释,以便您只能为同一事务中的所有托管实体拥有一个持久性上下文。 由于持久性上下文仅限于休眠会话,因此您需要知道会话可以包含几个事务,请参阅此答案以获取更多信息https://stackoverflow.com/a/5409180/1460591Spring Data 的工作方式是将事务连接在一起(称为Transaction Propagation)到一个事务(默认传播(必需))请参阅此答案以获取更多信息https://stackoverflow.com/a/25710391/1460591

如果您有多个独立的事务(例如,服务上没有事务注释),则要将事物连接在一起,因此您将有多个遵循 Spring Data 工作方式的会话,因此您有多个持久性上下文(又名第一级缓存),这意味着您可能会删除/修改持久上下文中的实体,即使使用flushAutomatically相同的删除/修改的实体可能已被获取并缓存在另一个事务的持久性上下文中,这将由于错误或未同步的数据而导致错误的业务决策。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么我们必须在 Data Jpa 中使用 @Modifying 注解进行查询 的相关文章

随机推荐