例如,我的 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 不能分析查询并理解它是一个修改查询?
注意!
使用
@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
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
,在持久性上下文中删除/修改实体也可能已在另一个事务的持久性上下文中获取并缓存相同的删除/修改实体,这会由于错误或未同步的数据而导致错误的业务决策。
欲了解更多详情,您可以点击此链接:-
http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html
@Modifying
注释的查询包括 INSERT、UPDATE、DELETE 和 DDL 语句。添加
@Modifying
注释表示该查询不是 SELECT 查询。
@Query
注释时,您应该使用选择查询 但是,您
@Modifying
注释可以在方法上方使用插入、删除、更新查询。