您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
浅析@Transactional注解原理及使用
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
浅析@Transactional注解原理及使用
自猿其说Tech
2021-10-14
IP归属:未知
43640浏览
计算机编程
事务管理是系统开发必不可少的一部分。Spring为我们提供了声明式事务和编程式事务;声明式事务是基于AOP,与编程式事务相比将业务逻辑和事务处理解耦,使代码不受污染。声明式事务有两种,一种在配置文件(XML)中做相应的规则声明,另一种基于@Transactional注解方式,注解配置是目前的主流方式。本文将浅析@Transactional注解的原理及如何使用。 ### 1 Spring@Transactional的注意事项 #### 1.@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚。默认遇到运行期异常(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的异常时回滚;而遇到需要捕获的异常(throw new Exception("注释");)不会回滚,即遇到受检查的异常(就是非运行时抛出的异常,编译器会检查到的异常叫受检查异常或说受检查异 常)时,需我们指定方式来**让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception。class,其它异常})。** #### 2.@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。 然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启了事务行为。 Spring团队的建议是你在**具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上**。你当然可 以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时, 那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团 队的建议并且在具体的类上使用 @Transactional 注解。 #### 3.@Transactional 注解标识的方法,处理过程尽量的简单。 尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以 将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。 @Transactional 的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的 方法,事务是不会起作用的。 ### 2 @Transactional之propagation (service中调用其他service时需要注意) 事务的传播行为 ![](//img1.jcloudcs.com/developer.jdcloud.com/7a01d6d6-67cc-4209-8075-27250ca876f520211013142153.png) ### 3 @Transactional 注解的属性信息 ![](//img1.jcloudcs.com/developer.jdcloud.com/429281d3-4f62-4dbf-84fd-d3ee8e99a59420211013142210.png) ### 4 性(4种): 即acid - 原子性 (atomicity):强调事务的不可分割. - 一致性 (consistency):事务的执行的前后数据的完整性保持一致. - 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰 - 持久性(durability) :事务一旦结束,数据就持久到数据库 ### 5 解决读问题: 设置事务隔离级别(5种) - DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别. 未提交读(read uncommited) :脏读,不可重复读,虚读都有可能发生 - 已提交读 (read commited):避免脏读。但是不可重复读和虚读有可能发生 - 可重复读 (repeatable read) :避免脏读和不可重复读.但是虚读有可能发生. - 串行化的 (serializable) :避免以上所有读问题. - Mysql 默认:可重复读 - Oracle 默认:读已提交 ### 6 @Transactional 事务实现机制 在应用系统调用声明了 @Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据 @Transactional 的属性配置信息,这个代理对象决定该声明 @Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。 Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,需要调用其 invoke 方法。 ![](//img1.jcloudcs.com/developer.jdcloud.com/806ce68f-849d-408c-b13a-cc0c2d1ab4b120211013142326.png) 正如上文提到的,事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager 来提供的,而具体的底层事务处理实现,由 P latformTransactionManager 的具体实现类来实现,如事务管理器 DataSourceTransactionManager。不同的事务管理器管理不同的数据资源 DataSource,比如 DataSourceTransactionManager 管理 JDBC 的 Connection。PlatformTransactionManager,AbstractPlatformTransactionManager 及具体实现类关系如图 2 所示。 ![](//img1.jcloudcs.com/developer.jdcloud.com/8f5c8b77-3091-4b80-abd7-17c71748327920211013142340.png) ### 7 具体实践 ##### 1.第一种情况,先插入,再查询,一个除数为0的bug异常,回滚了,没有插入成功。 ![](//img1.jcloudcs.com/developer.jdcloud.com/700b26a8-4679-4ce5-92f0-c764cbc496b820211013142410.jpg) ##### 2.使用try catch包裹异常代码,未回滚,添加成功了。 ![](//img1.jcloudcs.com/developer.jdcloud.com/3cd186c7-ba33-478e-8b77-69c9a3a5fe5f20211013142428.png) ##### 3.使用运行时RuntimeException异常throw,回滚了,未添加成功 ![](//img1.jcloudcs.com/developer.jdcloud.com/abff4fd4-d17d-45b1-9835-710a9ac29b8f20211013142445.png) ##### 4.方法上throws Exception 没有回滚,添加成功了。 ![](//img1.jcloudcs.com/developer.jdcloud.com/10fbe3fd-6933-4cc3-9f24-586176de2d1a20211013142604.png) ##### 5.使用rollbackFor指定异常回滚,并且throw了该异常,回滚了,未添加成功 ![](//img1.jcloudcs.com/developer.jdcloud.com/20400ce9-dcf0-4dc2-9655-c0fa2218e97d20211013142632.png) ##### 6.使用rollbackFor指定异常回滚,但是没有在代码中throw该异常,虽然在方法定义了throws该异常,为回滚,添加成功,跟第5个demo的 区别,请注意! 不懂的小伙伴看看throw和throws的区别,简单来说: throw 是语句抛出一个异常,一般是在代码块的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常 throws当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理 ![](//img1.jcloudcs.com/developer.jdcloud.com/4a7f9319-ade6-48d9-bdc9-dca5613e20bf20211013142649.png) ##### 7.add方法调用addtest方法,addtest方法有事务注解,事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一 个方法调用另一个方法有事务的方法,事务是不会起作用的。 ![](//img1.jcloudcs.com/developer.jdcloud.com/3f03c6d2-1fba-41bc-8599-f77a9758058420211013142711.png) ##### 8.add方法调用addtest方法,add方法有事务注解,即使addtest方法没有事务注解也是起作用的,回滚了,添加不成功,可以理解为把addte st方法体的内容copy到add方法中,根据前面的例子(单个方法加事务),这个是会回滚的。 ![](//img1.jcloudcs.com/developer.jdcloud.com/a8d33c75-3c31-42e1-a23c-15e1f5efea4a20211013142728.png) ##### 9.没问题,异常回滚了,两个都没有添加上 ![](//img1.jcloudcs.com/developer.jdcloud.com/c2730851-f007-4d32-abd1-56cfe7409a7920211013142746.png) ##### 10.OilMasterServiceImpl类的add方法有事务注解,调用OilTankerServiceImpl的add方法,发生异常,回滚了,两个都没添加上 ![](//img1.jcloudcs.com/developer.jdcloud.com/10f51f38-97e9-41d0-ba7f-1afebfbb3ce520211013142809.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/04ccab34-4889-4ed1-80fd-0ed7aa3ce10b20211013142821.png) ##### 11.OilMasterServiceImpl类的add方法没有事务注解,调用OilTankerServiceImpl的add方法(有事务注解),因为OilMasterServiceImpl 的add方法没有开启事务,所以OilMaster添加成功了,OilTankerServiceImpl的add方法有事务,改方法报异常了,回滚了,OilTanker没有添 加成功。 ![](//img1.jcloudcs.com/developer.jdcloud.com/84958441-f44f-4832-a663-f0e5f02070d020211013142839.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/a6b50a60-a1f6-4b57-90e7-0b8212fcb9a520211013142853.png) ##### 12.主方法调用,有异常,两者回滚,都没有插入成功 ![](//img1.jcloudcs.com/developer.jdcloud.com/417f7d23-9f26-48ab-bb90-7803269e2df320211013142911.png) ##### 13.使用了@Async异步注解,可以马上返回信息,不用等子方法调用结束,但是都没有回滚,两个都添加成功了 ![](//img1.jcloudcs.com/developer.jdcloud.com/5bf5fbe3-ca03-4cfa-844a-e8060d2071f820211013142931.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/b4bb7497-1a47-4dc8-b628-1dcf88979ddc20211013142944.png) ##### 14.OilMasterServiceImpl类的add方法没有事务注解,调用OilTankerServiceImpl的add方法(有事务注解),因为OilMasterServiceImpl 的add方法没有开启事务,所以OilMaster添加成功了,OilTankerServiceImpl的add方法有事务并且有@Async,因为有事务不会马上提交事务 ,时间到了往下走,方法报异常了,回滚了,OilTanker没有添加成功。 ![](//img1.jcloudcs.com/developer.jdcloud.com/e5d477d5-dd7e-4939-8604-15c8bb5d6eae20211013143010.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/0361597b-62a1-4099-92de-82f713edffdc20211013143019.png) ##### 15.如果主调用方法OilMasterServiceImpl加入事务注解,oilTankerService.add();跟13号例子一样保持不变,因为不在一个事务了,Oil Master会添加成,OilTanker还是会回滚,OilTanker没有保存上。 ![](//img1.jcloudcs.com/developer.jdcloud.com/eac1dc8a-3d4a-4c76-bb20-7211733fd0d620211013143037.png) ##### 16.虽然有点重复,把例子做全吧,主调用有事务,子调用,有try catch包裹异常代码,这里必须要throw这个异常,否则两者都会添加成功,本实例中,因为有throw该异常抛到外层,所以事务回滚了,两个 都没有添加成功。 ![](//img1.jcloudcs.com/developer.jdcloud.com/1be69219-b3cd-4b4b-ba87-5ce47795f31b20211013143100.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/0b275514-e8f0-4067-a068-3ec89064240f20211013143114.png) ##### 17.先看个奇怪的例子,图1回滚了,没有添加成事务,图2只是没有改下代码throw一个sql异常就没有回滚,添加成功了 因为Spring的默认的事务规则是遇到运行异常(RuntimeException及其子类)和程序错误(Error)才会进行事务回滚,显然SQLException并 不属于这个范围。如果想针对检测异常进行事务回滚,可以在@Transactional 注解里使用 rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚 这个throw e代码要看运气看看是否是RuntimeException的子类,对于我们很多的dao层调用,这个一定一定要注意了, ![](//img1.jcloudcs.com/developer.jdcloud.com/51250d74-2f8f-452a-b2b5-5bfc5c60dbef20211013143209.png) ![](//img1.jcloudcs.com/developer.jdcloud.com/8e097892-586e-49f1-a14b-261a8dabfab920211013143215.png) 下图指定了异常回滚,所以没有添加成功,回滚了,我们try catch这里要注意了 我们事务主要是针对数据库层面的,这个一定要注意了 ![](//img1.jcloudcs.com/developer.jdcloud.com/81aabbc0-7d83-4425-b991-d48d99030b4020211013143231.png) 以上就是我对@Transactional注解的理解,如有不对欢迎指正,希望对你有所帮助。 ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:攀登者小组(京喜达技术部 付国杨)
原创文章,需联系作者,授权转载
上一篇:Kafka Broker通信模型与顺序处理Producer消息的原理
下一篇:一只菜狗的ThreadLocal源码之旅
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号