Spring内部方法新起一个事务,此处应有坑。
来源:互联网 发布:依伊芭莎淘宝 编辑:程序博客网 时间:2024/05/22 17:30
本文(已获得作者授权)转载自http://www.cnblogs.com/yougewe/p/7466677.html
前言
事务可以使我们操作能够连贯起来,避免产生脏数据。而spring则是提供了一个更简单的方法,只要使用 @Transactional 注解,就可以保证操作的连贯性了。
@Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ)
声明在类上,对当前类的所有public方法都添加事务特性.如果声明在方法上则是增对当前方法有效.
原本这个方法运行得好好的,但是有一天,我们需要在这个方法里添加一个新业务操作,而且这个业务操作是不要求回滚的,类似于做日志记录一类的。WHAT SHOULD I DO ?
解决方案
由于业务的独特性,我能够快速想到的是,在这个类里面加一个private方法,然后直接去调用就ok了,如果说还是考虑到回滚的话,我也快速想到 @Transactional 的NOT_SUPPORTED传播特性,如:
@Transactional(propagation = Propagation.NOT_SUPPORTED)private void doMyExJob(UserDebitCardBean userDebitCard) { System.out.println("do my job..."); //do my job...}
这看起来很合理,没毛病。BUT…就是运行不起来,只要外面调用的方法一抛出异常,那么这个新方法的数据操作将会被回滚。妈蛋,到底哪里出了问题???仔细查了下资料,原来 @Transactional 注解由于原理决定了他只能作用于public方法中,而这里改为private,就完全被忽略无视了。OK,改呗:
@Transactional(propagation = Propagation.NOT_SUPPORTED)public void doMyExJob(UserDebitCardBean userDebitCard) { System.out.println("do my job..."); //do my job...}
感觉应该好了,然而并没有。我也是醉了,这个问题,如果仔细花时间,找原理是没有问题的,但是在关键时刻来这么一下,还是很不爽的。 网上看到一哥们说,还必须要将方法写到另一个类中,而且要通过spring的注入方式进行调用,才可以。好吧,那我就按照他的来,结果真的成功了。
//在接口中进行了定义,能够注入
@Override @Transactional(propagation = Propagation.NOT_SUPPORTED)public void doMyExJob(UserDebitCardBean userDebitCard) { System.out.println("do my job..."); //do my job...}
总算可以了,在赶时间的时候,能够解决问题的,就是好方法。至此,问题解决。
1. 使用public访问;
2. 写在外部类中,可被调用;
3. 使用注入
的方式进行该方法的执行。
下面我们来看看spring事务的讲解:
在配置文件中,默认情况下,会自动使用名称为transactionManager的事务管理器。所以,如果定义的事务管理器名称为transactionManager,那么就可以直接使用。如下:
<!-- 配置事务管理器 --><beanid="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"></bean><!-- enables scanning for @Transactional annotations --><tx:annotation-driven/>
一共有四个属性如下,
- mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理
- proxy-target-class:如果为true,Spring将创建子类来代理业务类;如果为false,则使用基于接口的代理。(如果使用子类代理,需要在类路径中添加CGLib.jar类库)
- order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。
transaction-manager:指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用
@Transactional的属性
- isolation 枚举org.springframework.transaction.annotation.Isolation的值 事务隔离级别
- noRollbackFor Class<? extends Throwable>[] 一组异常类,遇到时不回滚。默认为{}
noRollbackForClassName Stirng[] 一组异常类名,遇到时不回滚,默认为{}
propagation 枚举org.springframework.transaction.annotation.Propagation的值 事务传播行为
readOnly boolean 事务读写性
rollbackFor Class<? extends Throwable>[] 一组异常类,遇到时回滚
rollbackForClassName Stirng[] 一组异常类名,遇到时回滚
timeout int 超时时间,以秒为单位
value String 可选的限定描述符,指定使用的事务管理器
@Transactional标注的位置
@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。
如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,spring建议我们将@Transaction注解在实现类上。
在方法上的@Transactional注解会覆盖掉类上的@Transactional。
特别提醒
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
更新10-09
就上面的问题,对于我们在一个事务里面新建一个事务或者局部不适用事务,是为什么无法正常工作呢?
结合最近看AOP的理解,讨论一下其中的原因!
因为spring的service是通过动态代理生成的,当我们进入方法的时候会先进入invoke方法,此时会针对当前方法的注解去做判断和相应的配置,然后再通过反射调用方法,但是我们在方法执行时,局部调用的方法是无法进入到invoke的,因为没有新的代理对象生成,所以我们始终都是延续使用同一个代理对象和invoke方法的配置,这就是事务的传播性.所以当我们在另外一个方法中使用事务注解会被忽略掉
结语
一个巨坑,哈哈
本文写了一个小demo源代码地址
与君共勉!!!
- Spring,为内部方法新起一个事务,此处应有坑。
- Spring,为内部方法新起一个事务,此处应有坑。
- Spring,为内部方法新起一个事务,此处应有坑
- Spring内部方法新起一个事务,此处应有坑。
- Spring,为内部方法新起一个事务,此处应有坑
- spring方法内部调用方法事务关系
- 科大讯飞全球首发远场识别:智能硬件迎来新风口(此处应有掌声)
- spring事务 方法调用内部方法 事务不生效
- Spring事务拦截不到内部方法的互相调用
- spring事务,内部调用生效
- Spring声明事务方法
- spring 事务配置方法
- spring 事务配置方法
- java的一个日期格式加上指定月数,得到一个新日期(此方法通用)
- Spring -- 一个事务中独立开启事务
- Spring内部方法性能监控器
- 49.新起一个纯代码空白工程
- 声明一个类,它具有一个方法,此方法被重载三次,派生一个新类, 并增加一个新的重载方法,编写测试类验证四个方法对于派生类都有效
- 如何让ie8及之前的浏览器兼容HTML5新标签和自己设定的新标签
- HDOJ题目分类
- 手动创建活动之学习笔记
- 集合的实现2--使用数组
- mybatis源码环境搭建(写于2015-06-14 13:16:45)
- Spring内部方法新起一个事务,此处应有坑。
- 给定整数数组,除了一个元素之外,每个元素都会出现两次。 找到那个只出现一次的
- leetcode 463. Island Perimeter(C语言,二维数组,周长)23
- 归并排序
- Python:输入年月日判断是此年的第多少天
- cocos stuido的ccs文件加载成功 缺不显示图片的问题
- tomcat内存溢出问题记录
- 软考下午题之数据流图
- codeforce contest 103 problem D Time to Raid Cowavans(根号算法)