为什么我们必须在DataJpa中使用@Modifying注解进行查询

vlju58qv  于 2022-11-23  发布在  Spring
关注(0)|答案(3)|浏览(265)

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

rkttyhzu

rkttyhzu1#

注意!

使用@Modifying(clearAutomatically=true)将删除持久性上下文中托管实体上的任何挂起更新,spring声明如下:
这样做会触发注解到方法的查询作为更新查询,而不是选择查询。(请参阅EntityManager.clear()的JavaDoc以取得详细信息),因为这会有效地卸除EntityManager中所有尚未清除的变更。如果您希望自动清除EntityManager,您可以将@Modifying注解的clearAutomatically属性设置为true。
幸运的是,从Spring Boot 2.0.4.RELEASE开始,Spring Data添加了flushAutomatically标志(https://jira.spring.io/browse/DATAJPA-806),以便在执行修改查询检查参考www.example.com之前**自动刷新持久性上下文中的任何托管实体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的行之前或之后使用修改的对象,则使用clearAutomaticallyflushAutomatically,否则可以跳过这些标志
顺便说一句,这是为什么您应该总是将@Transactional注解放在服务层上的另一个原因,这样您就可以在同一个事务中为所有托管实体只拥有一个持久性上下文。您需要知道一个会话可以包含几个事务,请参阅此答案以了解更多信息https://stackoverflow.com/a/5409180/1460591 Spring Data 的工作方式是将事务(也称为事务隔离)合并为一个事务(默认隔离(必需))请参阅此答案以了解更多信息https://stackoverflow.com/a/25710391/1460591
如果有多个独立的事务,则将它们连接在一起(例如,在服务上没有事务注解)因此,您将有多个遵循spring data工作方式的会话,因此,您有多个持久性上下文,这意味着您可能会删除/修改持久性上下文中的实体,即使使用flushAutomatically,也可能会在另一个事务中获取和缓存相同的已删除/已修改实体'由于错误或未同步的数据,可能导致错误的业务决策

k4aesqcs

k4aesqcs2#

这将触发注解到方法的查询作为更新查询而不是选择查询。由于在执行修改查询之后EntityManager可能包含过时的实体,我们会自动清除它(有关详细信息,请参阅EntityManager.clear()的JavaDoc)。这将有效地删除仍在EntityManager中挂起的所有未刷新的更改。如果不希望自动清除EntityManager,可以设置@正在将注解的clearAutomatically属性修改为false;
欲知详情,请浏览此连结:
http://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html

qco9c6ql

qco9c6ql3#

需要@Modifying注解的查询包括INSERT、UPDATE、DELETE和DDL语句。
添加@Modifying注解指示该查询不是用于SELECT查询。

相关问题