环境
数据库: oracle 11g
JAR:
- org.springframework:spring-jdbc:4.3.8.RELEASE
- org.mybatis:mybatis:3.4.2
概念
REQUIRED(默认): 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。
REQUIRED_NEW: 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当前事务会被挂起。
早前对NEW的理解只是停留在: 当前方法会新开启一个事务,新事务的提交/回滚不会影响之前事务的提交/回滚。
但"当前事务会被挂起",那么NEW事务结束后,被挂起的当前事务应该是会恢复,但会恢复哪些东西了?
(摘自: 在Spring中使用PROPAGATION_REQUIRES_NEW带来的缓存问题?)
源码分析:
在TransactionTemplate的<T> T execute(TransactionCallback<T> action)中会通过TransactionManager的getTransaction方法来取得TransactionStatus
1、取得当前线程所关联的SessionHolder
2、若存在SessionHolder并且开启了事务(this.sessionHolder != null && this.sessionHolder.getTransaction() != null),而且当前的的传播行为为PROPAGATION_REQUIRES_NEW
3、挂起当前线程绑定的事务及其事务同步器,取消当前线程和当前session和connection的绑定,并保存所有的挂起信息以供恢复
4、创建新的session,并开启新的事务
5、执行TransactionTemplate的TransactionCallback回调
6、在新事务提交后,会恢复上个事务的所有信息和执行
测试问题描述
1、 method_A(T1事务)中查询了某条记录A(A.name = 1);然后调用method_B(T2事务,并且是REQUIRES_NEW )
2、在 method_B中修改了记录A并提交保存(A.name = 2);(method_B方法结束,T2事务提交)
3、然后回到 method_A 中再次查询这条记录A,发现得到的A.name = 1;但是此时数据库中的A.name = 2 。
用hibernate/mybatis得到的都是以上结果, 即在T1中查到的A.name = 1.
但是,我用spring的JdbcTemplate测试却发现: 本来在(3)得到A.name=1,但实际A.name =2。
为什么mybatis&hibernate得到的结果与JdbcTemplate不一样了?事务不是统一交给spring控制的吗?
(见问题: Spring事务REQUIRES_NEW的问题?)
个人理解
最初, 我以为在(3)一定会得到是A.name = 2。但实际在hibernate中得到的是A.name =1。(当时遇到问题用的是hibernate。)
然后我就重新去理解了REQUIRES_NEW, 发现应该是要得到A.name = 1, 即mybatis&hibernate得到的就是正确的。
但无意用JdbcTemplate测试却发现和mybatis&hibernate得到的不一样, 所以就迷茫了。完全不知道怎么理解。
现在在想一想, 到底什么是"事务控制"? 其实现在我也不知道怎么理解, 渣狗一只, 汪汪~~
可以参考:
spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!
spring事务源码解析 (这个可以细看)
但为什么觉得mybatis&hibernate的结果更正确呢?
1、更能体现出事务的一致性。
因为对t1事务来说,并没有改变过A.name。所以,从始至终都应该得到A.name =1。
一、spring boot公共部分
@SpringBootApplication public class TransactionApplication { public final static String ID = "1"; public static void main(String[] args) { SpringApplication app = new SpringApplication(TransactionApplication.class); app.run(args); } }