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

问题描述 投票:0回答:4

例如,我的 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 不能分析查询并理解它是一个修改查询?

java spring spring-data-jpa spring-annotations
4个回答
84
投票

注意!

使用

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

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

幸运的是,从

Spring Boot 2.0.4.RELEASE
开始,Spring Data添加了
flushAutomatically
标志(https://jira.spring.io/browse/DATAJPA-806)来在执行修改之前自动刷新持久性上下文上的任何托管实体查询检查参考https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/api/org/springframework/data/jpa/repository/Modifying.html#flushAutomatically

所以

最安全的使用方式@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 已经是 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/1460591 Spring数据的工作方式是将事务连接在一起(称为Transaction Propagation
)到一个事务中(默认传播(必需)),请参阅此答案以获取更多信息
https://stackoverflow.com/a/25710391/1460591

如果您有多个独立的事务(例如,服务上没有事务注释),则要将事物连接在一起,因此您将有多个遵循 Spring Data 工作方式的会话,因此您有多个持久性上下文(又名第一级缓存),这意味着您可能即使使用

flushAutomatically

,在持久性上下文中删除/修改实体也可能已在另一个事务的持久性上下文中获取并缓存相同的删除/修改实体,这会由于错误或未同步的数据而导致错误的业务决策。


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

欲了解更多详情,您可以点击此链接:-

http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html


13
投票
需要

@Modifying

 注释的查询包括 INSERT、UPDATE、DELETE 和 DDL 语句。

添加

@Modifying

 注释表示该查询不是 SELECT 查询。


1
投票
当您仅使用

@Query

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

© www.soinside.com 2019 - 2024. All rights reserved.