为什么我们必须将@Modifying注释用于数据JPA中的查询



例如,我的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。但是,这里的注释需要什么?为什么春季不能分析查询并了解它是一个修改的查询?

注意!

使用 @Modifying(clearAutomatically=true)将在持久性上下文中删除对托管实体的所有待处理更新,以下是:

这样做会触发对该方法的查询作为更新查询而不是选择一个。因为EntityManager可能包含执行修改查询后过时的实体,我们要做不能自动清除它(请参阅EntityManager.clear((的Javadoc有关详细信息(,因为这有效地删除了所有未融合的更改仍在EntityManager中进行审理。如果您希望EntityManager自动清除,您可以设置@modify antotation的clearautomply从true上归因于true。

幸运的是,从 Spring Boot 2.0.4.RELEASE spring数据开始添加了 flushAutomatically flag(https://jira.spring.io/browse/browse/datajpa-806(以自动在执行之前执行persistence上下文上的任何托管实体修改查询参考https://docs.spring.io/spring-data/jpa/docs/2.0.4.4.release/api/opi/org/springframework/data/data/jpa/jpa/repository/modifying.html#flushanomatomatomationalyationallomatomation

因此,使用@Modifying的最安全方法是:

@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是错误的

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&amp;flushAutomatically如果不是,则可以使用这些标志

跳过

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

如果您有多个孤立的交易(例如,在服务上没有交易注释(,则将事物连接在一起(,因此,您将按照弹簧数据工作的方式进行多个会话,因此您具有多个持久上下文(aka第一级缓存(,这意味着这意味着这意味着即使使用flushAutomatically,您也可以在持久性上下文中删除/修改实体,即使同一删除/修改的实体可能已经在另一个交易的持久性上下文中获取和缓存,这将由于错误或非同步数据而造成错误的业务决策。<</

这将触发注释的查询,以更新查询而不是选择一个。由于执行修改查询后,EntityManager可能包含过时的实体,因此我们会自动清除它(有关详细信息,请参见EntityManager.Clear((的Javadoc(。这将有效地删除EntityManager中仍在待处理的所有非冲洗更改。如果您不希望自动清除EntityManager,则可以将@Modify Antotation的ClearAutomplomical归因于false;

有关更多详细信息,您可以按以下链接: -

http://docs.spring.io/spring-data/jpa/docs/1.3.4.3.4.release/referene/html/jpa.repositories.html

需要@Modifying注释的查询包括插入,更新,删除和DDL语句。

添加@Modifying注释指示查询不适合选择查询。

仅使用@Query注释时,应使用选择查询但是,您可以使用@Modifying注释您可以在方法上方使用插入,删除,更新查询。

最新更新