为啥我们必须使用 @Modifying 注解在 Data Jpa 中进行查询
Posted
技术标签:
【中文标题】为啥我们必须使用 @Modifying 注解在 Data Jpa 中进行查询【英文标题】:why do we have to use @Modifying annotation for queries in Data Jpa为什么我们必须使用 @Modifying 注解在 Data Jpa 中进行查询 【发布时间】:2017-09-25 16:19:27 【问题描述】:例如,我的 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不能分析查询,明白是修改查询?
【问题讨论】:
@Transactional 注解在 Repository 中是不好的做法,最好在你的 Service 中使用它。原因一项业务操作(标记为事务)可能包含对 DB 的多个请求。甚至被几个DAO。更多这里***.com/questions/1079114/… @DanBrandt 除非您使用自定义方法的自定义 repo 实现必须将多个查询作为一个事务执行(eq. 计算所有记录,选择 10 个 id,按 id 选择记录,在 1 中返回所有这些数据对象)。 【参考方案1】:注意!
使用@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://***.com/a/5409180/1460591
spring data 的工作方式是将事务连接在一起(也称为事务隔离)到一个事务中(默认隔离(必需))请参阅此答案以获取更多信息https://***.com/a/25710391/1460591
如果您有多个事务(例如,在服务上没有事务注释),要将事物连接在一起,因此您将按照 spring 数据的工作方式进行多个会话,因此您有多个持久性上下文,这意味着您可能会删除/修改一个元素在持久性上下文中,即使使用flushAutomatically
,相同的已删除/修改的元素也可能会被提取并缓存在另一个事务的持久性上下文中,这会由于错误或未同步的数据而导致业务错误决策
【讨论】:
【参考方案2】:这将触发注释到方法的查询作为更新查询而不是选择一个。由于在执行修改查询后 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
【讨论】:
在最近的 JPA 版本中,clearAutomatically 和 flushAutomatically 两个标志都默认设置为 false,因此如果您想清除或刷新,则必须将标志设置为 true。 @justMe 那么你是说在最近的版本中,如果我们只包括修改注解而没有任何像flush或clear这样的属性,那么这个注解是没用的,什么都不做? @theprogrammer 我所说的是默认情况下设置为 false 的那些标志 doc here docs.spring.io/spring-data/jpa/docs/current/api/org/…,关于如果你不设置它们会发生什么?请参阅下面Younas的答案。或官方文档docs.spring.io/spring-data/jpa/docs/current/reference/html/…【参考方案3】:需要@Modifying
注释的查询包括 INSERT、UPDATE、DELETE 和 DDL 语句。
添加@Modifying
注释表示该查询不适用于SELECT 查询。
【讨论】:
以上是关于为啥我们必须使用 @Modifying 注解在 Data Jpa 中进行查询的主要内容,如果未能解决你的问题,请参考以下文章
SpringData 学习—— 使用 @Modifying 注解完成修改操作
SpringData系列四 @Query注解及@Modifying注解@Query注解及@Modifying注解
Spring Data JPA @Modifying 注解使用@Transactional