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