在开发过程中你是否遇到过这样的问题,当在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种传播行为:

  1. PROPAGATION_REQUIRED:支持当前事务,如果当前不存在事务则新建一个。
  2. PROPAGATION_SUPPORTS:支持当前事务,如果不存在,就不使用事务。
  3. PROPAGATION_MANDATORY:支持当前事务,如果不存在,则抛出异常。
  4. PROPAGATION_REQUIRES_NEW:如果当前有事务存在,挂起当前事务,创建一个新的事务。
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前有事务存在,挂起当前事务。
  6. PROPAGATION_NEVER:以非事务方式运行,如果当前有事务存在,抛出异常。
  7. PROPAGATION_NESTED:如果当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交和回滚。如果当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持参差不齐,使用时需注意。

不断分享开发过程用到的技术和面试经常被问到的问题,如果您也对IT技术比较感兴趣可以「关注」我

service和controller都加了事务_「Spring声明式事务」在service内部之间调用竟然失效啦?...相关推荐

  1. mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚

    mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 参考文章: (1)mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 (2)https://www.cnblog ...

  2. spring声明式事务

    11.声明式事务 11.1 事务回顾 把一组业务当做一个业务来坐,要么都成功,要么都失败! 事物在项目开发中的重要性不言而喻,关系到数据的一致性文件 确保完整性和一致性 事务的ACID原则 原子性(A ...

  3. Spring声明式事务管理、事务的传播行为xml配置

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 1. <tx:method name="insert*" propagat ...

  4. Spring声明式事务配置管理方法

    /*2011年8月28日 10:03:30 by Rush  */ 环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加 ...

  5. Spring声明式事务管理的配置详解

    环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add ...

  6. Spring 声明式事务在业务开发中容易碰到的坑总结

    Spring 声明式事务,在业务开发使用上可能遇到的三类坑,包括: 第一,因为配置不正确,导致方法上的事务没生效.我们务必确认调用 @Transactional 注解标记的方法是 public 的,并 ...

  7. 【Spring学习笔记 九】Spring声明式事务管理实现机制

    什么是事务?事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用,关乎数据准确性的地方我们一定要用到事务,防止业务逻辑出错. 什么是事务管理,事务管理对于企业应用而言至 ...

  8. Spring声明式事务原理

    本文我们将通过一个简单的例子回顾Spring声明式事务的使用,并通过源码解读内部实现原理,最后通过列举一些常见事务不生效的场景来加深对Spring事务原理的理解. 1. 案例 新建SpringBoot ...

  9. Spring的编程式事务声明式事务 基于注解的声明式事务控制

    文章目录 Spring中编程式事务 基于XML的声明式事务控制 基于注解的声明式事务控制 Spring集成web环境 Spring中编程式事务 Spring的事务控制可以分为编程式事务控制和声明式事务 ...

最新文章

  1. ubuntu更新rtl8192cu驱动
  2. tool class
  3. Linux 适用硬件平台/系统架构(i386 / i586 / i686 / x86 / x86_64)名词理解和区别
  4. Struts2-day2总结
  5. 如何获得完美的调色板?完美的配色素材专辑拿走!
  6. VC++中文件类型小结
  7. CAS在Tomcat中实现单点登录
  8. linux下启动jboss脚本,Linux下配置JBoss自动启动
  9. 苹果手机数据线充不了电_自动洗地机充不了电,洗地机厂家
  10. 轻轻松松背单词软件测试,完美单词王app
  11. 遥感方向SCI期刊整理
  12. 密西西比河谷州立大学:Android应用程序开发(一)
  13. 2015CDAS中国数据分析师行业峰会:R语言量化投资数据分析应用
  14. 【华为OD机试真题 C++】数字涂色 【2022 Q4 | 100分】
  15. 什么是堆、栈?堆和栈的区别
  16. 首都师范 博弈论 3 3 1求解二人零和博弈
  17. [一个程序员的人文素养系列]《情感勒索》摘抄
  18. 2023年1月12日,openKylin 0.9.5正式发布!
  19. 努比亚红魔5G救砖线刷教程
  20. 复制文件或文件夹时出错(无法复制***:没有足够的可用磁盘空间

热门文章

  1. 将安卓手机打造成你的python全栈开发利器
  2. Query框架学习第九天:jQuery工具函数介绍与使用
  3. C#开源资源大汇总(2)
  4. ASP.NET中Server.MapPath() 和Request.MapPath()使用
  5. 小红书笔记api_超级干货丨小红书种草笔记如何写?
  6. leetcode53. 最大子序和详解——pygo
  7. STM32 - 定时器高级应用说明 - 多触波的实现 (N-pulse waveform generation using timer synchronization)- 01
  8. 概率论和数理统计 - 01
  9. 【小程序】【Tips】等待服务器的反馈的定时、间隔运行的正确方法
  10. c语言程序设计点亮第一个LED,实验2__C语言程序设计及_LED跑马灯实验.ppt