Java 为什么我们必须在数据Jpa中使用@modify注释进行查询
例如,我的CRUD界面中有一个方法,可以从数据库中删除用户: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
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
}