@Modifying注解和@Transactional注解?

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

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 }
123456

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

一、@Modifying注解
  在官方资料中,给出了这样几句说明:

复制代码
As the queries themselves are tied to the Java method that executes them, you can actually bind them directly by using the Spring Data JPA @Query annotation
rather than annotating them to the domain class.

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.

Doing so triggers the query annotated to the method as an updating query instead of a selecting one.
复制代码
  如下:

@Modifying
@Query(“update User u set u.firstname = ?1 where u.lastname = ?2”)
int setFixedFirstnameFor(String firstname, String lastname);
  第一句话的意思是可以用@Query注解来将自定义sql语句绑定到自定义方法上。

第二句话的意思时,可以用@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 }
12345678910

复制代码
  第四句话的意思是,当加上@Modifying注解时,JPA会以更新类语句来执行,而不再是以查询语句执行。

也就是说,当我们要通过自已写的更新、插入、删除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  }
1234567

复制代码
  但是,此时,该方法还不完整,执行时程序会报以下错误:

复制代码
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException:
Executing an update/delete query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:402)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)

at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:398)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1585)

复制代码
二、@Transactional注解
  官方的说明:

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 }
12345678

复制代码
  这句话的意思是,默认情况下,repository 接口中的CRUD方法都是被@Transactional注解修饰了的,对于读的操作方法,@Transactional注解的readOnly属性是被设置为true的,即只读;CRUD中的其他方法被@Transactional修饰,即非只读。如果你需要修改repository 接口中的某些方法的事务属性,可以在该方法上重新加上@Transactional注解,并设置需要的属性。

我们先来看一下,@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 }
12345678910111213141516

复制代码
  由上可见@Transactional注解的readOnly默认的属性的false,即非只读,当一个事务是非只读事务的时候,我们可以进行任何操作。

再看一下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 }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778

复制代码
  从SimpleJpaRepository源码中可以看出:

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

   2)该类的所有查询类操作方法都与类相同,都拥有只读事务;

   3)该类的所有保存、更新、删除操作方法都用@Transactional重新注解了(默认readOnly=false)。
123

说明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 }
12345678

复制代码
  至此,该方法按所期望的结果运行成功了。

三、@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 }
12345678910

复制代码
  该注解中有两个属性:flushAutomatically、clearAutomatically,从字面理解是自动刷新和自动清除。

自动刷新,即执行完语句后立即将变化内容刷新到磁盘,如果是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);
1234

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/294713.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python中的类以及对象 与 self关键字的介绍

Python中的类 类的定义 在Python中,类是一种用户定义的复杂数据类型 类提供了一种组织代码和数据的方式,用来创建现实世界对象的蓝图 一个类,是由成员变量(属性)和函数(类的方法)组成的 |------成员属性 类------||------类的方法定义格式 class Teacher: #-----类名de…

打工人的2.0时代,只需要一副AR眼镜!

在数字化时代&#xff0c;工业行业中的生产效率如何得到提升&#xff1f;工业AR眼镜或许是一个不错的选择。不过工业AR眼镜真的可以协助员工处理工作中所遇到的各种问题吗&#xff1f;我们以制造业、医疗行业、船舶业的不同从业者为例&#xff1a; 假如你是一名制造业从业者&am…

这些流行的K8S工具,你都用上了吗

关注【云原生百宝箱】公众号&#xff0c;获取更多云原生消息 本文介绍了一些流行的 Kubernetes 工具和常见的集群组件。例如 Helm 作为 Kubernetes 应用的包管理器&#xff0c;以及本地开发所需的 Kubernetes 发行版。另外提及了一些常见的集群组件&#xff0c;如集群自动缩放器…

C/C++输入函数总结

1、cin 2、cin.get 3、cin.getline 4、getline 5、gets 6、getchar 1、cin 可以接受单个字符和字符串&#xff0c;但遇空格,"TAB","回车"结束&#xff01;&#xff01;&#xff01; 若不跳过空白字符&#xff0c;使用 noskipws 流控制。 使用方法如…

nacos 2.* 部署在linux服务器无法注册问题

通过sdk注册代码 报错 Exception in thread "main" ErrCode:-401, ErrMsg:Client not connected, current status:STARTING at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:639) at com.alibaba.nacos.common.remote.client…

Python 使用 selenium 进行自动化测试或者协助日常工作

Python 使用 selenium 进行自动化测试 或者协助日常工作&#xff0c;内容如下所示&#xff1a; 1、基础准备 需要准备 Python 环境需要安装 selenium 模块 pip install selenium 需要下载谷歌浏览器驱动程序 &#xff0c;下载地址为 下载对应版本驱动程序 查询谷歌浏览器版本…

STM32疑难杂症

1.keil的奇怪问题 创建的数组分配内存到0x10000000地址的时候,数据总是莫名其妙的出现问题,取消勾选就正常了 stm32f407内部有一个CCM内存,这部分内存只能由内核控制,任何外设都不能够进行访问。这样问题就来了,如果使用keil5进行编程时勾选了这个选项(下图),则编译的…

Linux———cat命令详解

目录 cat 命令是 Linux 中用于查看文件的内容或将多个文件合并输出。 基本语法&#xff1a; 常用选项&#xff1a; 示例用法&#xff1a; 查看文件的内容&#xff1a; ​编辑 将多个文件的内容合并输出&#xff1a; ​编辑 显示每一行的行号&#xff1a; ​编辑 显示非…

TemporalKit的纯手动安装

最近在用本地SD安装temporalkit插件 本地安装插件最常见的问题就是&#xff0c;GitCommandError:… 原因就是&#xff0c;没有科学上网&#xff0c;而且即使搭了ladder&#xff0c;在SD的“从网址上安装”或是“插件安装”都不行&#xff0c;都不行&#xff01;&#xff01;&am…

函数式编程及应用

目录 什么是Lambdalambda表达式的类型及实现方式类型语法 常用函数式接口Customer 函数式编程在Stream中的应用总结参考资料 什么是Lambda Lambda 表达式是 JDK8 的一个新特性&#xff0c;可以取代大部分的匿名内部类&#xff0c;写出更优雅的Java代码。 Lambda 表达式描述了一…

Spring国际化的应用及原理详解

1. 简介 Spring国际化&#xff08;Spring Internationalization&#xff0c;简称i18n&#xff09;是Spring框架提供的一种机制&#xff0c;用于支持多语言的应用程序。它使得开发者能够轻松地在应用程序中实现不同语言的支持&#xff0c;从而满足全球化的需求。通过Spring国际…

Selenium-java 定位元素时切换iFrame时的方法

具体方法如下图所示&#xff0c;如果iFrame中嵌套多层iFrame需要逐层定位到需要的那一层iFrame,完成操作后&#xff0c;执行该代码&#xff1a;driver.switchTo() .defaultContent() ; 是返回最顶部的frame

C语言编译器(C语言编程软件)完全攻略(第二部分:与编译器相关的几个知识点)

介绍常用C语言编译器的安装、配置和使用。 二、与编译器相关的几个知识点 上节我们介绍了编译器和 IDE 的概念&#xff0c;大家肯定希望赶紧实践一下&#xff0c;用 IDE 真正地运行一段C语言代码来看看效果&#xff0c;这样能够更快地获得成就感。 但是&#xff0c;使用 IDE …

网络故障问题一般性检查排查思路

一、基本连通性检查 在网络中ping是一个十分强大的TCP/IP工具。它可以用来检测网络的连通情况和分析网络速度、也可以ping网址根据域名得到服务器IP、同时我们根据ping返回的TTL值来判断对方所使用的操作系统及数据包经过路由器数量。 ping 网址&#xff0c;有几种输出情况&a…

开源项目go-admin的代码生成功能使用

go-admin代码生成功能 具体如何配置go-admin项目的可以查看首次使用go-admin进行配置启动go-admin&#xff0c;再看下一篇文章&#xff01; 找到代码生成 先进入到界面&#xff0c;点击开发工具&#xff0c;点击代码生成 代码生成操作 1. 创建表&#xff0c;可以使用可视…

【RK3399 PCIE调试——硬件信息资源获取】

一、1、 硬件接口 二、2、 PCB原理图 三、 官网地址&#xff1a; https://t.rock-chips.com/portal.php 相关资料和固件烧写可参考资料下载菜单

Linux GDB 调试

文章目录 一、Qemu二、Gdbvscode 调试 三、RootFs 一、Qemu qemu 虚拟机 Linux内核学习 Linux 内核调试 一&#xff1a;概述 Linux 内核调试 二&#xff1a;ubuntu20.04安装qemu Linux 内核调试 三&#xff1a;《QEMU ARM guest support》翻译 Linux 内核调试 四&#xff1a;…

半导体Memory的分类

文章目录 略图introRAM & ROM 略图 intro 存储器是嵌入式系统中用于存放数据和程序的模块。有些存储器是MCU内置的&#xff0c;有些是扩展的。 存储器嵌入式系统中常见且重要的外设模块。搞清楚存储器的分类是从事嵌入式开发工作的一项基本功。 从功能上&#xff0c;存储器…

利用小红书笔记详情API:为内容运营提供强大的支持

利用小红书笔记详情API&#xff0c;内容运营者可以获得对小红书平台上的笔记内容的深入洞察&#xff0c;从而为其运营工作提供强大的支持。以下是该API如何支持内容运营的几个关键方面&#xff1a; 获取笔记内容与数据&#xff1a; API允许内容运营者直接获取小红书平台上的笔记…

视频号小店开店需要准备什么资料?

我是电商珠珠 很多想要开通视频号小店的新手并不知道开通视频号小店需要准备什么资料。 今天我就来给大家详细的讲一下。 视频号小店分为三种类型&#xff0c;一种是普通企业店&#xff0c;一种是个体工商店&#xff0c;还有一种是品牌企业店。 先来说说普通企业店 1、营业…