为什么我们必须对 Data Jpa 中的查询使用 @Modifying 注释

新手上路,请多包涵

例如,我的 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 分析查询并理解它是一个修改查询?

原文由 Artyom Emelyanenko 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
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

原文由 Anshul Sharma 发布,翻译遵循 CC BY-SA 3.0 许可协议

警告!

使用 @Modifying(clearAutomatically=true) 将删除持久性上下文中托管实体的任何待处理更新 spring 状态如下:

这样做会触发注释给方法的查询作为更新查询而不是选择查询。由于在执行修改查询后 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 已经是 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 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 -93-4-2700 那么你可以跳过使用这些标志

顺便说一句,这是您应该始终在服务层上放置 @Transactional 注释的另一个原因,以便您只能为同一事务中的所有托管实体提供一个持久性上下文。由于持久性上下文绑定到休眠会话,您需要知道一个会话可以包含几个事务,请参阅此答案以获取更多信息 https://stackoverflow.com/a/5409180/1460591 spring 数据的工作方式是它加入了将多个事务(也称为事务隔离)合并为一个事务(默认隔离(必需)),请参阅此答案以获取更多信息 https://stackoverflow.com/a/25710391/1460591

如果您有多个孤立的事务(例如,在服务上没有事务注释),则将事物连接在一起,因此您将有多个会话遵循 spring 数据的工作方式,因此您有多个持久性上下文,这意味着您可能会删除/修改一个实体持久性上下文,即使使用 flushAutomatically 相同的已删除/修改的实体可能已被提取并缓存在另一个事务的持久性上下文中,这会由于错误或未同步的数据而导致错误的业务决策

原文由 Youans 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题