service和controller都加了事务_「Spring声明式事务」在service内部之间调用竟然失效啦?...
在开发过程中你是否遇到过这样的问题,当在Controller中调用Service中A()方法,A方法内部又调用Service中B()方法,由于A方法中只有查询操作所以没有加事务控制,B方法中含有多次修改操作所以增加了@Transactional注解,结果在A方法调用完B方法后,程序报错了,但是B方法中修改操作的数据竟然成功了,这是什么鬼,必须开启了探索Spring事务之路,直接上示例。
A方法无事务,B方法加事务
@RestControllerpublic class Controller{ @Autowired private StudentcardService studentcardService; @RequestMapping(value = "/test/{id}}", method = RequestMethod.GET) public Response queryStudentCard(@PathVariable("id") String id) { studentcardService.updateA(id); }}
@Servicepublic class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override public void updateA(String id) { //先去调用内部方法B this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override @Transactional public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; }}
访问后执行结果如下:
然而事务并没有起作用~接着进行测试。
将A方法加事务,B方法不加事务
@Servicepublic class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; }}
访问后执行结果如下:
事务起作用了,都没有修改成功!接下来我们来个增强版,加上try后看下会有怎样的效果。
A方法加事务,B方法没有事务,但是在A调用B方法时用try进行包裹,B方法中有错误
@Servicepublic class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B try { this.updateB(id); }catch (Exception e){} StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; }}
访问后执行结果如下:
由于报错被try包起来了,所以数据都插入了!那如果将报错信息放到执行完方法B后呢,会怎样呢?
A方法加事务,B方法没有事务,但是在A调用B方法时用try进行包裹,A方法中有错误
@Servicepublic class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override @Transactional public void updateA(String id) { //先去调用内部方法B try { this.updateB(id); }catch (Exception e){} StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; } @Override public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); }}
访问后执行结果如下:
哇塞,数据都没有插入呢!这是因为在事务提交前报错了,事务全部rollback了,下面言归正传,示例1为何不能成功呢?于是查询各种资料终于找到了缘由,并对示例1进行改造。
A方法无事务,B方法加事务
@Service@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)public class StudentcardServiceImpl implements StudentcardService { @Resource private StudentCardMapper studentCardMapper; @Override public void updateA(String id) { //先去调用内部方法B StudentcardService studentcardService = (StudentcardService) AopContext.currentProxy(); studentcardService.updateB(id); //this.updateB(id); StudentCard sc =new StudentCard(); sc.setScId(id); //修改问题字段 sc.setQuestion("AAAAA"); studentCardMapper.update(sc); } @Override @Transactional public void updateB(String id) { StudentCard sc =new StudentCard(); sc.setScId(id); //修改答案字段 sc.setAnswer("BBBBB"); studentCardMapper.update(sc); //修改完数据后报错 double i=1/0; }}
访问后执行结果如下:
哈哈,数据没有进行修改,事务起作用了!下面让我们来一探究竟。
示例1 事务没有起作用,是由于Spring事务本质是基于AOP代理来实现的,当Controller调用Service的方法A是基于proxy的,所以会切入,但是方法A在调用方法B时,属于类内部调用,即使方法B上加上了@Transactional注解,但没有Spring代理了,所以不受事务控制,自然事务不会生效。
debug service代理对象
this对象
示例2 事务可以起作用是由于事务的传播行为导致的,默认事务的传播行为为:PROPAGATION_REQUIRED 。方法A标注了注解@Transactional ,执行的时候传播给方法B,因为方法A开启了事务,线程内的connection的属性autoCommit=false,并且执行到方法B时,事务传播依然是生效的,得到的还是方法A的connection,autoCommit还是为false,所以事务生效;反之,如果方法A没有注解@Transactional 时,是不受事务管理的,autoCommit=true,那么传播给方法B的也为true,执行完自动提交,即使B标注了@Transactional 事务也是不起作用的。
示例5 事务又可以起作用的,是由于我们在方法A调用方法B时,先获取到了Service的当前代理,然后用当前代理去调用方法B,所以事务当然会生效了~
顺便补充下事务的传播行为,事务的传播行为是为了解决业务层方法之间相互调用,产生的事务应该如何进行传递的问题。spring有如下7种传播行为:
- PROPAGATION_REQUIRED:支持当前事务,如果当前不存在事务则新建一个。
- PROPAGATION_SUPPORTS:支持当前事务,如果不存在,就不使用事务。
- PROPAGATION_MANDATORY:支持当前事务,如果不存在,则抛出异常。
- PROPAGATION_REQUIRES_NEW:如果当前有事务存在,挂起当前事务,创建一个新的事务。
- PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前有事务存在,挂起当前事务。
- PROPAGATION_NEVER:以非事务方式运行,如果当前有事务存在,抛出异常。
- PROPAGATION_NESTED:如果当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交和回滚。如果当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持参差不齐,使用时需注意。
不断分享开发过程用到的技术和面试经常被问到的问题,如果您也对IT技术比较感兴趣可以「关注」我
service和controller都加了事务_「Spring声明式事务」在service内部之间调用竟然失效啦?...相关推荐
- mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚
mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 参考文章: (1)mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 (2)https://www.cnblog ...
- spring声明式事务
11.声明式事务 11.1 事务回顾 把一组业务当做一个业务来坐,要么都成功,要么都失败! 事物在项目开发中的重要性不言而喻,关系到数据的一致性文件 确保完整性和一致性 事务的ACID原则 原子性(A ...
- Spring声明式事务管理、事务的传播行为xml配置
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 1. <tx:method name="insert*" propagat ...
- Spring声明式事务配置管理方法
/*2011年8月28日 10:03:30 by Rush */ 环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加 ...
- Spring声明式事务管理的配置详解
环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add ...
- Spring 声明式事务在业务开发中容易碰到的坑总结
Spring 声明式事务,在业务开发使用上可能遇到的三类坑,包括: 第一,因为配置不正确,导致方法上的事务没生效.我们务必确认调用 @Transactional 注解标记的方法是 public 的,并 ...
- 【Spring学习笔记 九】Spring声明式事务管理实现机制
什么是事务?事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用,关乎数据准确性的地方我们一定要用到事务,防止业务逻辑出错. 什么是事务管理,事务管理对于企业应用而言至 ...
- Spring声明式事务原理
本文我们将通过一个简单的例子回顾Spring声明式事务的使用,并通过源码解读内部实现原理,最后通过列举一些常见事务不生效的场景来加深对Spring事务原理的理解. 1. 案例 新建SpringBoot ...
- Spring的编程式事务声明式事务 基于注解的声明式事务控制
文章目录 Spring中编程式事务 基于XML的声明式事务控制 基于注解的声明式事务控制 Spring集成web环境 Spring中编程式事务 Spring的事务控制可以分为编程式事务控制和声明式事务 ...
最新文章
- ubuntu更新rtl8192cu驱动
- tool class
- Linux 适用硬件平台/系统架构(i386 / i586 / i686 / x86 / x86_64)名词理解和区别
- Struts2-day2总结
- 如何获得完美的调色板?完美的配色素材专辑拿走!
- VC++中文件类型小结
- CAS在Tomcat中实现单点登录
- linux下启动jboss脚本,Linux下配置JBoss自动启动
- 苹果手机数据线充不了电_自动洗地机充不了电,洗地机厂家
- 轻轻松松背单词软件测试,完美单词王app
- 遥感方向SCI期刊整理
- 密西西比河谷州立大学:Android应用程序开发(一)
- 2015CDAS中国数据分析师行业峰会:R语言量化投资数据分析应用
- 【华为OD机试真题 C++】数字涂色 【2022 Q4 | 100分】
- 什么是堆、栈?堆和栈的区别
- 首都师范 博弈论 3 3 1求解二人零和博弈
- [一个程序员的人文素养系列]《情感勒索》摘抄
- 2023年1月12日,openKylin 0.9.5正式发布!
- 努比亚红魔5G救砖线刷教程
- 复制文件或文件夹时出错(无法复制***:没有足够的可用磁盘空间
热门文章
- 将安卓手机打造成你的python全栈开发利器
- Query框架学习第九天:jQuery工具函数介绍与使用
- C#开源资源大汇总(2)
- ASP.NET中Server.MapPath() 和Request.MapPath()使用
- 小红书笔记api_超级干货丨小红书种草笔记如何写?
- leetcode53. 最大子序和详解——pygo
- STM32 - 定时器高级应用说明 - 多触波的实现 (N-pulse waveform generation using timer synchronization)- 01
- 概率论和数理统计 - 01
- 【小程序】【Tips】等待服务器的反馈的定时、间隔运行的正确方法
- c语言程序设计点亮第一个LED,实验2__C语言程序设计及_LED跑马灯实验.ppt