注意!
使用@Modifying(clearAutomatically=true) 将删除持久性上下文中托管实体上的任何挂起更新,弹簧状态如下:
这样做会触发注释到方法的查询作为更新
查询而不是选择一个。由于 EntityManager 可能包含
执行修改查询后过时的实体,我们做
不会自动清除它(参见 EntityManager.clear() 的 JavaDoc
有关详细信息),因为这有效地删除了所有未刷新的更改
仍待在 EntityManager 中。如果您希望 EntityManager
自动清除,可以设置@Modifying注解的
clearAutomatically 属性为 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
// JOHN still exist since john with active being false was not
// flushed into the database when @Modifying kicks in
}
场景 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 exist 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 data 的工作方式是将事务连接在一起(也称为事务隔离)到一个事务中(默认隔离(必需))请参阅此答案以获取更多信息https://stackoverflow.com/a/25710391/1460591
如果您有多个事务(例如,在服务上没有事务注释),要将事物连接在一起,因此您将按照 spring 数据的工作方式进行多个会话,因此您有多个持久性上下文,这意味着您可能会删除/修改一个元素在持久性上下文中,即使使用flushAutomatically,相同的已删除/修改的元素也可能会被提取并缓存在另一个事务的持久性上下文中,这会由于错误或未同步的数据而导致业务错误决策