Java 为什么我们必须在数据Jpa中使用@modify注释进行查询

Java 为什么我们必须在数据Jpa中使用@modify注释进行查询,java,spring,spring-data-jpa,spring-annotations,Java,Spring,Spring Data Jpa,Spring Annotations,例如,我的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") in

例如,我的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);
}
公共接口CrudUserRepository扩展了JpaRepository{
@交易的
@修改
@查询(“从用户u中删除,其中u.id=:id”)
int delete(@Param(“id”)int-id、@Param(“userId”)int-userId);
}

此方法仅适用于注释@modify。但是这里需要什么注释呢?为什么spring不能分析查询并理解它是一个修改查询?

这将触发该方法注释为更新查询而不是选择查询的查询。由于EntityManager在执行修改查询后可能包含过时的实体,因此我们会自动清除它(有关详细信息,请参阅EntityManager.clear()的JavaDoc)。这将有效地删除EntityManager中仍挂起的所有未刷新的更改。如果不希望自动清除EntityManager,可以将@Modifying annotation的clearAutomatically属性设置为false

有关更多详细信息,请访问以下链接:-


需要修改注释的查询包括INSERT、UPDATE、DELETE和DDL语句

添加
@Modifying
注释表示该查询不适用于SELECT查询

小心

使用
@Modifying(clearAutomatically=true)
将删除持久性上下文中托管实体上的任何挂起更新,spring声明如下:

这样做会触发注释到方法的查询作为更新 查询而不是选择一个。正如EntityManager可能包含的那样 在执行修改查询之后,我们会 不自动清除(请参阅EntityManager.clear()的JavaDoc) 详细信息),因为这将有效地删除所有未刷新的更改 仍在EntityManager中挂起。如果您希望EntityManager 如果自动清除,则可以设置@Modifying注释的 clearAutomatically属性为true

幸运的是,从
Spring Boot 2.0.4.开始,在执行修改查询检查引用之前,释放添加的
Spring数据
flushsautomatically
flag(),以自动刷新持久性上下文上的所有托管实体

因此,使用
@修改
最安全的方法是:

@Modifying(clearAutomatically=true, flushAutomatically=true)
如果我们不使用这两个标志会发生什么情况??

考虑以下代码:

repo {
   @Modifying
   @Query("delete User u where u.active=0")
   public void deleteInActiveUsers();

}
场景1为什么
自动冲洗

 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为什么
自动清除

下面是考虑JuneServer。Actudio已经是错误的

因此,如果-在同一事务中-您在执行
@修改
的行之前或之后玩修改的对象,则使用
自动清除
&
自动清除
,如果不是,则可以跳过使用这些标志

顺便说一句,这也是为什么您应该始终在服务层上放置
@Transactional
注释的另一个原因,这样您在同一事务中的所有托管实体只能有一个持久性上下文。 由于持久性上下文绑定到hibernate会话,所以您需要知道一个会话可以包含两个事务。有关更多信息,请参阅此答案 spring数据的工作方式是将事务连接在一起(也称为事务隔离)成为一个事务(默认隔离(必需))。有关更多信息,请参阅此答案


如果您有多个事务(例如,服务上没有事务),则将事物连接在一起因此,您将有多个会话遵循spring数据的工作方式,因此您有多个持久性上下文,这意味着您可能会删除/修改持久性上下文中的元素,即使自动使用
flush
,相同的删除/修改的元素也可能会被提取并缓存到另一个事务的持久性中context

@存储库中的事务性注释是一种不好的做法,最好在您的服务中使用它。原因一个业务操作(标记为事务)可能包含对DB的多个请求。甚至用几把刀。这里有@DanBrandt的更多信息,除非您使用自定义方法执行自定义回购协议,该方法必须作为一个事务执行多个查询(即:计算所有记录,选择10个id,按id选择记录,在1个对象中返回所有数据)。在最新的JPA版本中,默认情况下,标志clearAutomatically和FlusAutomatically都设置为false,因此如果要清除或刷新,则必须将标志设置为true。如果我们不使用这两个属性,会发生什么情况更新的回答后能否请您在此处解释关于@Transactional on Service layer的内容?这对你的处境有什么好处?正如我从代码中看到的,无论是在存储库层还是服务层,结果都是一样的。@Max更新了答案,请参见最后一节
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
}