spring事务很多人都知道是基于aop 切面实现的,在目标方法执行前
try{
connection.setAutoCommit(false);
@Transactional
目标方法A{
--》方法B{}
}
connection.setAutoCommit(true);
}catch(){
connection.rollback();
}finally{
connection.close();
}
这个大概的逻辑可能大部分人都知道,但是事务是怎么传递的呢,
当程序执行到内层方法B的时候,它怎么能感知方法A上面的事务注解,或者说程序怎么知道当前线程存在事务,
spring会使用ThreadLocal来保存当前线程的事务信息(就是@Transactional里面默认的值或者声明的值),包括事务的传播属性和隔离级别,回滚异常类型等。
中途插播一下关于connection的知识,这个对象就是一次会话,里面有一个sessionID标识,同一次事务共用一个connection对象。还有一个安全保存点概念要了解一下。connection.setSavepoint 就是代码执行一部分了,人为调用这行代码,以保证如果接下来出现异常也不会回滚安全点之前的操作。
写到这里就比较明白了,如果内层事务想和外层事务不相互影响,则要么就是使用不同connection,要么就是设置安全保存点,保证内层事务出现问题也不会回导致外层的事务回滚。
如果方法B上的事务注解上的传播属性为不支持事务的时候,其实就是先设置安全保存点,然后设置connection的是默认的自动提交模式connection.setAutoCommit(true);
如果B方法是传播属性为Propagation.REQUIRES_NEW,则是会新建一个connection,对于 数据库来说这两个connection完全没有关系,外层的错误将不会影响内层方法B,因为对于B来说是个独立的事务,它已经完事了,因为外层出现的异常不会抛到内层来,但是如果方法B抛出异常,则内层事务会回滚,因为异常被往外层抛出,则外层事务也一样回滚,本来这两个事情本来没啥关系,就是Java的异常抛出机制就是内层往外层丢出,没办法,伤及池鱼咯,先说这么多,相信你已经足够有能力应对面试官的拷问了。
connection 还告诉我们,所谓隔离级别,说白了就是这行代码connection.setTransactionIsolation(level);spring只是告诉连接对象说,我要使用什么隔离级别而已。它只是个传话的角色。
private ThreadLocal<Connection> connLocal = new ThreadLocal<Connection>(); spring通过线程本地变量来同享connection来实现多个dao方法的执行对数据库来说都源自同一个connection对象,自然就是同一次事务。只要任意方法抛出被声明的异常,只要任意方法调用connection.rollback();connection.setAutoCommit(true);即可实现回滚,而不是所有方法都调用rollback();
暂时先写到这,以后我发现我存在认知偏差,我也会第一时间修改博客。