文章目录

  • 一、亲身案例
  • 二、改进方式
  • 三、原理分析

该篇博客为总结自己曾写下的Bug

一、亲身案例

当时的场景为:在controller层获取一笔交易单的信息(前台传给controller层为Map类型的键值对),然后controller层直接将这个Map参数对象转发给Service层,在Service层进行一个对象属性的封装,继而调用mapper层接口完成数据的持久化。

问题出现在Service层,由于这一笔交易单信息会涉及多张表的数据信息,为了代码结构的优美,我特地在Service层的方法中,调用同类中的另一个doSave()方法来完成数据持久化保存。最后再潇洒的为doSave()方法添加一个@Transactional注解,扬长而去。

悄悄的说,因为我自己在测试过程中就发现了问题,但是无奈自己不知道具体的原因。我便将最容易出错的那一个保存操作提到最前然,最不容易出错的放在了最后面。

将当时的代码抽象成下面的形式,此时的@Transactional注解,可以说是加了,但是又没有完全加上。

@Component
public class MobianServiceImpl implements MobianService {@Overridepublic int saveInfo(InfoVo infoVo) {User user = new User();Hobby hobby = new Hobby();// 组装对象过程。。。user.setId(infoVo.getUserId());user.setName(infoVo.getUserName());hobby.setHobby(infoVo.getHobbyName());hobby.setUserId(infoVo.getUserId());// 具体的保存操作。。。int i = doSave(user, hobby);return i;}@Transactionalpublic int doSave(User user, Hobby hobby){int i = mobianMapper.saveUser(user);// 发生异常int err = 1/0;int i1 = mobianMapper.saveHobby(hobby);return i + i1;}
}

二、改进方式

1、根据@Transactional注解的原理出发(这个方式显得很呆)

@Component
public class MobianServiceImpl implements MobianService {@Autowiredprivate MobianMapper mobianMapper;@Autowiredprivate MobianServiceImpl mobianService;@Overridepublic int saveInfo(InfoVo infoVo) {User user = new User();Hobby hobby = new Hobby();// 组装对象过程。。。user.setId(infoVo.getUserId());user.setName(infoVo.getUserName());hobby.setHobby(infoVo.getHobbyName());hobby.setUserId(infoVo.getUserId());// 具体的保存操作。。。// 获取Spring容器中的对象int i = mobianService.doSave(user, hobby);return i;}@Transactionalpublic int doSave(User user, Hobby hobby){int i = mobianMapper.saveUser(user);// 发生异常int err = 1/0;int i1 = mobianMapper.saveHobby(hobby);return i + i1;}
}

2、将doSave方法写在其他的service类中,用一个service类调用另一个service类

3、直接在saveInfo方法上面添加@Transactional注解

4、int i = ((MobianServiceImpl ) AopContext.currentProxy()).doSave(user, hobby)

从底层出发,去AOP容器中获取对应的被动态处理后的doSave方法,不过还需要你补充对应的配置,配置如下:

  1. 通过注解添加配置(加在类上)
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
  1. 或通过xml配置文件添加配置
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

这样就解决了报错问题。```


三、原理分析

Spring中对于数据库事务的约定,其实现原理是AOP,而AOP又是通过动态代理去实现。(AOP是在Spring的生命周期中完成)。

以我们上面代码中为例,我们在方法内部调用doSave(),等同于this.doSave(),此处的this表示new MobianServiceImpl()对象,即调用这个类中的这个方法完成持久化。这是一个自调用的形式。

解决方法一,方式一之所以能够实现,是因为我们去Spring容器中获取到的mobianService容器对象不等于我们直接new出来的对象。有看过我之前文章的小伙伴就应该知道,他们为什么不是同一个:浅谈Spring中Bean的生命周期,Spring容器中获取的是new出来的对象的代理对象,前者包含后者。换句话说,Spring容器中的对象经过了AOP处理(处理@Transactional注解),所以具有事务的特点。但是我们直接new出来的对象,仅仅是一个对象,不具备事务的特点。

解决方法二,也是相同的原理来理解,因为我们由一个service调用另一个service时,另一个对象就是去Spring容器中获取的,这样就能以一种看似不起眼的方式转移解决方法一中看起来很瓜的方式。

解决方法三,直接在最顶层添加事务,这种方式很粗暴,可能会出现在某些复杂的业务场景中不适用的情况。

该Bug解决的场景为,写下Bug代码半年后的某天早晨,因为睡不着,起来看书突然就看到了这么一段对于这个注解的描述,瞬间茅塞顿开,后面去公司马上找到以前的代码,发现果然是这个问题。我真菜!!!

@Transactional注解失效场景之——同类中方法调用,事务失效相关推荐

  1. 在同类中方法互调事务失效

    同类中A方法未加@Transactional,B方法加@Transactional A调用B,B的事务注解并未生效. 众所周知,事务是通过Aop动态代理实现的,当A方法不加@Transactional ...

  2. 【每日提高之声明式事物】spring声明式事务 同一类内方法调用事务失效

    2019独角兽企业重金招聘Python工程师标准>>> [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话"自从用了Spring AOP啊,事务管理真轻松啊, ...

  3. spring声明式事务 同一类内方法调用事务失效

    一 宏观说明 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话"自从用了Spring AOP啊,事务管理真轻松啊,真轻松:事务管理代码没有了,脑不酸了,手不痛了,一口气全配上 ...

  4. Spring中同一个类中方法调用事务不生效,非事务方法调用事务方式事务不生效

    我们假定在SerivceXXX中有两个方法: serviceA 非事务方法 serviceB事务方法 如果serviceA中方法定义类似如下: public void serviceA(){..... ...

  5. java 同类调用方法_JAVA中同类中方法的调用问题?

    1.为什么可以直接new+构造方法,来创建对象.而不用创建实例引用呢,像常见的这种:Car car1=new Car;我记得看过有说java中没有被引用的对象将被GC处理.那这个算不算. 你创建了一个 ...

  6. 深入浅出JVM(五)之Java中方法调用

    方法调用 要知道Java中方法调用唯一目的就是确定要调用哪一个方法 方法调用可以分为解析调用和分派调用,接下来会详细介绍 非虚方法与虚方法 非虚方法: 静态方法,私有方法,父类中的方法,被final修 ...

  7. Spring事务总结(一) 内部调用事务失效、异常回滚

    Spring事务总结(一) 内部调用事务失效.异常回滚 参考文章: (1)Spring事务总结(一) 内部调用事务失效.异常回滚 (2)https://www.cnblogs.com/gss128/p ...

  8. @transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!...

    Spring注解(如@Transactional.@Cacheable.@Async等),在使用不当时,很可能会失效.失效的情况有很多种,本文我们就来瞅瞅,为啥同一个类中普通方法调用Spring注解方 ...

  9. Spring service本类中方法调用另一个方法事务不生效问题

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

  10. Java中方法调用参数传递的方式是传值,尽管传的是引用的值而不是对象的值。(Does Java pass by reference or pass by value?)

    原文地址:http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html 在Java中,所有的对象变量都是引用,Java通 ...

最新文章

  1. java获取正则表达式_JAVA 正则表达式(获取)
  2. Java如何调用dll
  3. TensorFlow 教程——电影评论文本分类
  4. 读《系统虚拟化-原理与实现》-第二章
  5. openwrt 遍译php_openwrt安装编译
  6. oracle linux6 u盘安装,U盘安装RHEL6
  7. 电影评论分类:二分类问题
  8. mysql创建联合唯一索引
  9. Git 的BUG小结
  10. 125w短波通信距离_陕西烽火通信短波及超短波产品.doc
  11. 谓语动词时态 - 一般过去时、一般现在时、现在进行时
  12. python抓取谷歌app市场的icon
  13. SVG_16_defs标签_use标签_style标签_红绿灯效果
  14. 用c语言解参数积分,C语言求定积分的通用函数
  15. 安霸Ambarella_海思Hisilicon_AI芯片参数对比
  16. 【excel】开启了循环引用怎么关闭
  17. 北京区块链技术应用协会第一届第四次会员大会顺利召开
  18. 微信扫码充值 php,微信扫码支付,php版
  19. 手机百度浏览器ua标识在哪里_荣耀play浏览器ua标识在哪里
  20. 根据起始点经纬度、距离、方位角计算目标点经纬度的方法

热门文章

  1. Luogu4712「生物」能量流动
  2. 前端模拟数据(mock数据)的方法一:访问该项目的静态资源目录
  3. 三本学计算机没用吗,腾讯员工:我211学计算机都觉得写代码难,三本的人能写代码为啥考不上211...
  4. python time sleep 阻塞 异步_python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)...
  5. java中md5校验工具,java实现MD5文件校验
  6. php 后端服务错误定义,【后端开发】php常见的错误类型有哪四种
  7. Docker从理论到实践(六)------搭建和使用本地私有Docker镜像仓库
  8. MySQL(10)-----多表创建及描述表关系(一对多的分析和实现)
  9. MapReduce编程模型简介和总结
  10. 指定等级 Exercise07_01