@Modifying注解和@Transactional注解?

@Modifying注解和@Transactional注解?JPA 中自定义的插入 更新 删除方法为什么要添加 Modifying 注解和 Transactiona 注解 前几天 有个同事在使用 JPA 的自定义 SQL 方法时 程序一直报异常 捣鼓了半天也没能解决 咨询我的时候 我看了一眼他的程序 差不多是这个样子的 1 Repository2p amp l

1 @Repository 2 public interface UserRepository extends JpaRepository<User,Long> { 
    3 4 @Query(value = "delete from pro_user where id = ?1",nativeQuery = true) 5 void deleteUserById(Long id); 6 } 

我告诉他,你的deleteUserById方法缺少了@Modifying注解和@Transactional注解,他半信半疑地试了一下,然后果然就解决了。其实,如果他查一下官方资料或许很快也就能找到答案。基于这个背景,本文详细讲解一下为何我们自定义的插入、更新、删除操作需要加@Modifying注解和@Transactional注解。

You can modify queries that only need parameter binding by annotating the query method with @Modifying

The @Modifying annotation is only relevant in combination with the @Query annotation. Derived query methods or custom methods do not require this Annotation.

第二句话的意思时,可以用@Modifying注解来标注只需要绑定参数的自定义的更新类语句(更新、插入、删除)。

第三名话的意思是说@Modifying只与@Query联合使用,派生类的查询方法和自定义的方法不需要此注解,如:

复制代码

 1 @Repository 2 public interface UserRepository extends JpaRepository<User,Long> { 
    3 4 // 父类的保存方法 5 @Override 6 User save(User entity); 7 8 // 按照JPA语法规则自定义的查询方法 9 List<User> findFirst10ByLastname(String lastName, Pageable pageable); 10 } 

也就是说,当我们要通过自已写的更新、插入、删除SQL语句来实现更新、插入、删除操作时,至少需要用两个步骤:

@Query来注入我们自定义的sql;

使用@Modifying来标注是一个更新类的自定义语句。

按照这个规则,修改同事的那个方法:

复制代码

1 @Repository 2 public interface UserRepository extends JpaRepository<User,Long> { 
    3 4 @Modifying 5 @Query(value = "delete from pro_user where id = ?1",nativeQuery = true) 6 void deleteUserById(Long id); 7 } 

By default, CRUD methods on repository instances are transactional. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies. For details, see JavaDoc of SimpleJpaRepository. If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows:

Example. Custom transaction configuration for CRUD

复制代码

1 public interface UserRepository extends CrudRepository<User, Long> { 
    2 3 @Override 4 @Transactional(timeout = 10) 5 public List<User> findAll(); 6 7 // Further query method declarations 8 } 

我们先来看一下,@Transactional注解的源码:

复制代码

 1 @Target({ 
   ElementType.METHOD, ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Inherited 4 @Documented 5 public @interface Transactional { 
    6 7 Propagation propagation() default Propagation.REQUIRED; 8 9 Isolation isolation() default Isolation.DEFAULT; 10 11 int timeout() default -1; 12 13 boolean readOnly() default false; 14 15 // 其他省略 16 } 

再看一下repository 接口的实现类SimpleJpaRepository的源码(只摘了部分源码):

复制代码

 1 @Repository 2 @Transactional( 3 readOnly = true 4 ) 5 public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> { 
    6 7 @Transactional 8 public void deleteById(ID id) { 
    9 Assert.notNull(id, "The given id must not be null!"); 10 this.delete(this.findById(id).orElseThrow(() -> { 
    11 return new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", this.entityInformation.getJavaType(), id), 1); 12 })); 13 } 14 15 @Transactional 16 public void delete(T entity) { 
    17 Assert.notNull(entity, "The entity must not be null!"); 18 this.em.remove(this.em.contains(entity) ? entity : this.em.merge(entity)); 19 } 20 21 @Transactional 22 public void deleteAll(Iterable<? extends T> entities) { 
    23 Assert.notNull(entities, "The given Iterable of entities not be null!"); 24 Iterator var2 = entities.iterator(); 25 26 while(var2.hasNext()) { 
    27 T entity = var2.next(); 28 this.delete(entity); 29 } 30 } 31 32 public T getOne(ID id) { 
    33 Assert.notNull(id, "The given id must not be null!"); 34 return this.em.getReference(this.getDomainClass(), id); 35 } 36 37 public List<T> findAll() { 
    38 return this.getQuery((Specification)null, (Sort)Sort.unsorted()).getResultList(); 39 } 40 41 public List<T> findAll(@Nullable Specification<T> spec) { 
    42 return this.getQuery(spec, Sort.unsorted()).getResultList(); 43 } 44 45 public List<T> findAll(@Nullable Specification<T> spec, Sort sort) { 
    46 return this.getQuery(spec, sort).getResultList(); 47 } 48 49 public <S extends T> long count(Example<S> example) { 
    50 return executeCountQuery(this.getCountQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType())); 51 } 52 53 public <S extends T> boolean exists(Example<S> example) { 
    54 return !this.getQuery(new SimpleJpaRepository.ExampleSpecification(example), example.getProbeType(), (Sort)Sort.unsorted()).getResultList().isEmpty(); 55 } 56 57 @Transactional 58 public <S extends T> S save(S entity) { 
    59 if (this.entityInformation.isNew(entity)) { 
    60 this.em.persist(entity); 61 return entity; 62 } else { 
    63 return this.em.merge(entity); 64 } 65 } 66 67 @Transactional 68 public <S extends T> S saveAndFlush(S entity) { 
    69 S result = this.save(entity); 70 this.flush(); 71 return result; 72 } 73 74 @Transactional 75 public void flush() { 
    76 this.em.flush(); 77 } 78 } 

1)该类上注解了只读事务@Transactional(readOnly = true);

 2)该类的所有查询类操作方法都与类相同,都拥有只读事务; 3)该类的所有保存、更新、删除操作方法都用@Transactional重新注解了(默认readOnly=false)。 

说明JPA为我们提供的所有方法,包括JPA规则的自定义方法在其底层都为我们做好了事务处理,而我们自定义的方法需要自己来标注事务的类型是只读还是非只读。根据这个原理,再次修改开篇所列出的方法:

复制代码

1 @Repository 2 public interface UserRepository extends JpaRepository<User,Long> { 
    3 4 @Transactional 5 @Modifying 6 @Query(value = "delete from pro_user where id = ?1",nativeQuery = true) 7 void deleteUserById(Long id); 8 } 

三、@Modifying注解补充说明

复制代码 1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({ 
    ElementType.METHOD, ElementType.ANNOTATION_TYPE }) 3 @Documented 4 public @interface Modifying { 
    5 6 boolean flushAutomatically() default false; 7 8 boolean clearAutomatically() default false; 9 } 

自动刷新,即执行完语句后立即将变化内容刷新到磁盘,如果是insert语句操作,则与JPA的 S saveAndFlush(S entity);方法效果相同;

自动清除,即执行完语句后自动清除掉已经过期的实体,比如,我们删除了一个实体,但是在还没有执行flush操作时,这个实体还存在于实体管理器EntityManager中,但这个实体已经过期没有任何用处,直到flush操作时才会被删除掉。如果希望在删除该实体时立即将该实体从实体管理器中删除,则可以将该属性设置为true,如:

1 @Modifying(clearAutomatically = true) 2 @Transactional 3 @Query(value = "delete from pro_user where id = ?1",nativeQuery = true) 4 void deleteUserById(Long id); 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227973.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午8:05
下一篇 2026年3月16日 下午8:05


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号