为什么我们必须对 Data Jpa 中的查询使用 @Modifying 注释
why do we have to use @Modifying annotation for queries in Data 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。但是这里的注解有什么用呢?为什么不能spring分析查询并理解它是修改查询?
这将触发为方法注释的查询作为更新查询而不是选择查询。由于 EntityManager 在执行修改查询后可能包含过时的实体,我们会自动清除它(有关详细信息,请参阅 EntityManager.clear() 的 JavaDoc)。这将有效地删除所有仍在 EntityManager 中挂起的未刷新更改。如果您不希望 EntityManager 被自动清除,您可以将 @Modifying 注释的 clearAutomatically 属性设置为 false;
有关详细信息,您可以按照此 link:-
http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html
需要 @Modifying
注释的查询包括 INSERT、UPDATE、DELETE 和 DDL 语句。
添加 @Modifying
注释表示查询不是 SELECT 查询。
CAUTION!
使用 @Modifying(clearAutomatically=true)
将删除持久性上下文中托管实体的任何未决更新 spring 说明如下:
Doing so triggers the query annotated to the method as an updating
query instead of a selecting one. As the EntityManager might contain
outdated entities after the execution of the modifying query, we do
not automatically clear it (see the JavaDoc of EntityManager.clear()
for details), since this effectively drops all non-flushed changes
still pending in the EntityManager. If you wish the EntityManager to
be cleared automatically, you can set the @Modifying annotation’s
clearAutomatically attribute to true.
还好,从Spring Boot 2.0.4.RELEASE
开始Spring数据添加了flushAutomatically
标志(https://jira.spring.io/browse/DATAJPA-806) to auto flush any managed entities on the persistence context before executing the modifying query check reference 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)
What happens if we don't use those two flags??
考虑以下代码:
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
注释的另一个原因,这样您在同一事务中只能为所有托管实体提供一个持久性上下文。
由于持久性上下文绑定到休眠会话,您需要知道一个会话可以包含几个事务,请参阅此答案以获取更多信息
spring 数据的工作方式是它将事务连接在一起(也称为事务隔离)成为一个事务(默认隔离(必需)),请参阅此答案以获取更多信息
如果您有多个事务(例如,服务上没有事务注释),则将事物连接在一起,因此您将按照 spring 数据工作方式进行多个会话,因此您有多个持久性上下文,这意味着您可能 delete/modify 持久性上下文中的一个元素,即使使用 flushAutomatically
相同的 deleted/modified 元素也可能被获取并缓存在另一个事务的持久性上下文中,这将导致由于错误或 un-synced数据
例如,我的 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分析查询并理解它是修改查询?
这将触发为方法注释的查询作为更新查询而不是选择查询。由于 EntityManager 在执行修改查询后可能包含过时的实体,我们会自动清除它(有关详细信息,请参阅 EntityManager.clear() 的 JavaDoc)。这将有效地删除所有仍在 EntityManager 中挂起的未刷新更改。如果您不希望 EntityManager 被自动清除,您可以将 @Modifying 注释的 clearAutomatically 属性设置为 false;
有关详细信息,您可以按照此 link:-
http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html
需要 @Modifying
注释的查询包括 INSERT、UPDATE、DELETE 和 DDL 语句。
添加 @Modifying
注释表示查询不是 SELECT 查询。
CAUTION!
使用 @Modifying(clearAutomatically=true)
将删除持久性上下文中托管实体的任何未决更新 spring 说明如下:
Doing so triggers the query annotated to the method as an updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we do not automatically clear it (see the JavaDoc of EntityManager.clear() for details), since this effectively drops all non-flushed changes still pending in the EntityManager. If you wish the EntityManager to be cleared automatically, you can set the @Modifying annotation’s clearAutomatically attribute to true.
还好,从Spring Boot 2.0.4.RELEASE
开始Spring数据添加了flushAutomatically
标志(https://jira.spring.io/browse/DATAJPA-806) to auto flush any managed entities on the persistence context before executing the modifying query check reference 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)
What happens if we don't use those two flags??
考虑以下代码:
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
注释的另一个原因,这样您在同一事务中只能为所有托管实体提供一个持久性上下文。
由于持久性上下文绑定到休眠会话,您需要知道一个会话可以包含几个事务,请参阅此答案以获取更多信息
spring 数据的工作方式是它将事务连接在一起(也称为事务隔离)成为一个事务(默认隔离(必需)),请参阅此答案以获取更多信息
如果您有多个事务(例如,服务上没有事务注释),则将事物连接在一起,因此您将按照 spring 数据工作方式进行多个会话,因此您有多个持久性上下文,这意味着您可能 delete/modify 持久性上下文中的一个元素,即使使用 flushAutomatically
相同的 deleted/modified 元素也可能被获取并缓存在另一个事务的持久性上下文中,这将导致由于错误或 un-synced数据