spring中的事务
spring中的事务
事务的基础
在目前的spring中,大家都知道,spring定义了7种事务的传播属性,简要如下
- Required 当前方法一定有事务,如果前面没有事务,就新建一个事务,如果前面有事务,就加入到前面的事务
- Requires_New 当前方法一定有事务,如果前面没有事务,就新建一个事务,如果前面有事务,就挂起当前事务,再新建一个事务
- Supports 当前方法支持事务,如果前面没有事务,就不使用事务,如果前面有事务,就加入前面的事务
- NotSupported 当前方法不支持事务,如果前面没有事务,就不使用事务,如果前面有事务,就挂起当前事务
- Mandatory 当前方法强制需要事务,如果前面没有事务,就会抛出TransactionRequiredException异常
- Never 当前方法强制不需要事务,如果前面有事务,就会抛出异常
- Nest 使用基于JDBC3.0的嵌套事务
这7种传播属性,在org.springframework.transaction.TransactionDefinition种定义,具体定义各种度娘都能查到,这边不在赘述.
说点大家不知道的,spring对事务的定义,不是spring原创,而是基于EJB的CMT的规范定义的,其中的6种和CMT种的定义完全一致,最后的Nest,是spring基于JDBC3.0种,数据库可能会支持嵌套事务,增加了1种.
弄个面试题
事务在整个系统里面占据了绝对的核心位置.如果事务都没玩好,那系统一定是烂的.所以,很有必要时不时把事务拿来说一说.
在真实的编码场景中,我们最常用的事务传播行为就是Required和Requires_New.这边也分享一个面试题,一般不要直接问事务的传播行为,事务那么复杂,怎么能简单做个问答题就搞定呢??
我这边一般是基于实际的代码,来问事务的结果,更能看出候选人对于技术的掌握.
假设有这么一段代码,声明了2个服务,ServiceA和ServiceB
class ServiceA{
@事务
a(){
insert()
serviceB.b();
update()
}
aa(){
a();
}
}
class ServiceB{
@事务
b(){
update();//问题1
}
}
//外部调用
serviceA.aa();
serviceA.a();围绕上述的代码,可以一层一层的深入的问问题.
第一步,如果外部调用serviceA.aa()方法,请问程序执行到问题1的地方,容器创建了多少个事务??后续紧跟着的问题,就是为什么是这个数字??
如果有人能在回答数字前,问我一下,@事务有什么参数么?我会很有好感,并且提示,假设就是@Tansactional,没有修饰.
如果后面的为什么回答的比较好,这个数字没回答对,逻辑上也没啥问题,我还会提示,你觉得调用serviceA.aa()方法和调用serviceA.a()方法,结果一样么??
第二步,如果正常调用serviceA.a()方法,如果执行到serviceB.b()方法异常了,请问前面的insert的数据会写入数据库么??
同时,如果我在调用完serviceB.b()方法以后,后面在serviceA.a()方法的update异常了,前面serviceB.b()方法的update数据会写入数据库么??
如果回答不能,那么如何修改上述的代码就能了呢??
第三步,在我面试的人中,很少有人能到这个步骤,因为前面的第二步,就是对事务的传播行为进行提问的.第三步,就是修改传播行为,把serviceB.b()方法的传播行为改成NotSupported,再重复上面的问题.
一般情况,会有一半以上的人在第一个问题的时候,就直接退出了.当然,spring事务的处理,还远不止上面的场景,但是通过一个伪代码场景,可以大范围的排除掉一些靠死记硬背的同学.各位看官,如果想尝试一下,可以留言上面三个步骤里面问题的答案.
弄点原理
目前还没有人能完整的通过上面所有的问题,但是,我也会偶尔问一下第四个问题,无论是Requires_New还是NotSupported,都有挂起的动作,具体的挂起,是怎么实现的??或者是,能不能说说,Requires_New是如何实现挂起前面的事务,再创建一个新事务的??
现在网络上的软文,大部分的源码都会告诉你,spring处理事务的源码在org.springframework.transaction.support.AbstractPlatformTransactionManager,其中处理传播行为的,大部分在handleExistingTransaction方法里面.
这部分的源码,这边就不在赘述了,主要就是spring定义的事务传播行为的实现.
但是真正spring在处理事务的内部逻辑的,其实是org.springframework.transaction.support.TransactionSynchronizationManager.
其实在TransactionSynchronizationManager中,定义了6个ThreadLocal变量,spring在处理事务的传播行为的时候,就是通过对这6个ThreadLocal变量中间的数据转换来实现的挂起.
提示
一个ThreadLocal(A)是存储当前事务变量,另外一个ThreadLocal(B)是存储过往的事务变量.
挂起,说的简单点,就是把A的存储移动到B里面,并且清空A
说的再深入一点,就是在JDBC定义操作事务的时候,是使用Connection操作事务的,所以事务的挂起,不但是ThreadLocal中的事务对象的转换,还会牵涉当前Connection的转换,Requires_New的新建事务,本质上是从DataSource里面再取了一个Connection,单独建立的事务.
如果让你实现事务,你也会这么做
总结
EJB从0迭代开发到3.0,当时作为Sun的亲儿子,为什么没有干过Spring???另外Spring可能被替代么??
如果单纯的从IOC容器看,外部有很多竞争者,比如google的Guice,无论是血统还是背景,都厉害的很,但是Guice也没有干过Spring,为什么??
从我的从业经历来看,spring当时被引入中国,大范围被使用,就是事务管理赢得了当时大部分技术人员的共识.
