前言
本文主要介绍的是在Spring
框架中有关事务的应用方式,以及一些生产中常见的与事务相关的问题、使用建议等。同时,为了让读者能够更容易理解,本文在讲解过程中也会通过源码以及案例等方式进行辅助说明,通过阅读本文不但能够解决在实际生产环境中遇到的与Spring
事务相关的问题,也能让你掌握更好的应用方式。
事务的传播类型
我们首先从事务的传播类型开始说起,在Spring
中事务传播指的是:一个事务方法调用了另一个事务方法,比如事务方法A
在执行过程中又调用了事务方法B
,那么对于事务方法B
来说,应该如何如何运行?于是,针对这个问题,Spring
就定义了七种数据传播类型,基本上就是罗列了所有可能遇到的场景,然后供使用者选择:
-
REQUIRED:支持当前事务,如果当前不存在则新开启一个事务(默认配置)
-
SUPPORTS:支持当前事务,如果当前不存在事务则以非事务方式执行
-
MANDATORY:支持当前事务,如果当前不存在事务则抛出异常
-
REQUIRES_NEW:创建一个新事务,如果当前已存在事务则挂起当前事务
-
NOT_SUPPORTED:以非事务方式执行,如果当前已存在事务则挂起当前事务
-
NEVER:以非事务方式执行,如果当前已存在事务则抛出异常
-
NESTED:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务
场景分类
事务传播类型 | 前一个方法存在事务 | 前一个方法不存在事务 | 当前一个方法存在事务时,是否与其是同一个事务 |
---|---|---|---|
REQUIRED | 使用前一个方法的事务 | 创建一个新的事务 | 是 |
SUPPORTS | 使用前一个方法的事务 | 按非事务方式执行 | 是 |
MANDATORY | 使用前一个方法的事务 | 抛出异常 | 是 |
REQUIRES_NEW | 创建一个新的事务 | 创建一个新的事务 | 否 |
NOT_SUPPORTED | 以非事务方式执行 | 按非事务方式执行 | - |
NEVER | 抛出异常 | 按非事务方式执行 | - |
NESTED | 嵌套方式使用前一个方法的事务 | 创建一个新的事务 | 是 |
如果你还不清楚这些传播类型具体有什么区别也没关系,接下来本节会针对每一种类型分别进行效果演示,以此来帮助你进行理解。
在此之前,先准备一下演示的数据
sql
CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
两条更新语句,分别对t1
、t2
两张表进行更新。
xml
<update id="updateT1">
update t1 set c = 2 where id = 1
</update>
<update id="updateT2">
update t2 set c = 2 where id = 1
</update>
REQUIRED
含义:支持当前事务,如果当前不存在则新开启一个事务(默认配置)。
下面这块代码逻辑为:当调用T1Service
中的func
方法时,除了要更新t1
表数据之外,还会调用t2Service
中的func
方法,更新t2
表,不过,在执行t2Service
中的方法时会遇到异常。
java
@Service
public class T1Service {
@Resource
private TestMapper testMapper;
@Resource
private T2Service t2Service;
@Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
}
}
@Service
public class T2Service {
@Resource
private TestMapper testMapper;
@Transactional
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
}
@Transactional
默认的传播方式就是REQUIRED
,当方法执行到int i = 1 / 0
时会抛出异常,此时t1、t2
表中的数据都不会被修改,因为这两个方法使用的是同一个事务,所以只要有一个遇到异常,两个更新就都不会成功。
SUPPORTS
含义:支持当前事务,如果当前不存在事务则以非事务方式执行。
t2Service
的func
方法现在没有事务了,t2Service
的func
方法配置上@Transactional(propagation = Propagation.SUPPORTS)
,当执行int i = 1 / 0
时,t1、t2
两张表数据都不会回滚。
java
@Service
public class T1Service {
@Resource
private TestMapper testMapper;
@Resource
private T2Service t2Service;
// 去掉事务 @Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
}
}
java
@Service
public class T2Service {
@Resource
private TestMapper testMapper;
/**
* 数据不会回滚,因为当前没有事务,SUPPORTS会以非事务方式执行
* 如果配置成 @Transactional(propagation = Propagation.REQUIRED),则事务会生效
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
}
MANDATORY
含义:支持当前事务,如果当前不存在事务则抛出异常。
当t1Service
没有事务时,把t2Service
的func
方法配置为@Transactional(propagation = Propagation.MANDATORY)
java
// t1Service
public void func() {
testMapper.updateT1();
t2Service.func();
}
// t2Service
@Transactional(propagation = Propagation.MANDATORY)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
异常信息
java
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]
REQUIRES_NEW
含义:创建一个新事务,如果当前已存在事务则挂起当前事务。
t2
的数据不会被更新。
java
// t1Service
public void func() {
testMapper.updateT1();
t2Service.func();
}
// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
与REQUIRED有什么区别呢?
如果把抛出异常的地方放到t1Service
中。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
}
// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
testMapper.updateT2();
}
再次执行后,t2
的数据不会回滚,t1
的数据会回滚,因为t2
和t1
不是一个事务。
NOT_SUPPORTED
含义:以非事务方式执行,如果当前已存在事务则挂起当前事务。
NOT_SUPPORTED
的效果就是无论异常是在t1Service
还是t2Service
都不会回滚t2
的数据。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
}
// t2Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
NEVER
含义:以非事务方式执行,如果当前已存在事务则抛出异常。
如字面含义,直接抛出异常。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
}
// t2Service
@Transactional(propagation = Propagation.NEVER)
public void func() {
testMapper.updateT2();
}
java
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:413) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:352) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]
如果把t1Service
中的事务去掉,则不会抛出不支持事务的异常,同时t2Service
自己抛出异常后,数据也是不会回滚的,完全是当做无事务处理。
java
// t1Service
public void func() {
testMapper.updateT1();
t2Service.func();
}
// t2Service
@Transactional(propagation = Propagation.NEVER)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
NESTED
含义:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务。
NESTED
应该是几种事务传播方式中最难理解的,如果不注意,NESTED
和REQUIRED
功能看起来则差不多,都可以理解为有事务则加入,没有则新启一个,但实际上NESTED
比REQUIRED
要更加灵活。
先来看第一个案例,在t1Service
中调用t2Service
时,对t2Service
抛出的异常进行了捕获,并且自己也没有再抛出。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
// 以此catch异常的原因是想像你说明,在t1Service的func方法中,是不会因为调用t2Service遇到异常而被回滚的,因此异常已经被catch了。回滚主要是因为使用的是同一个事务。
try {
t2Service.func();
} catch (Exception e) {
e.printStackTrace();
}
}
// t2Service
@Transactional(propagation = Propagation.REQUIRED)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
当t2Service
配置为REQUIRED
时,t1
、t2
都进行了回滚,因为是同一个事务。
但如果t2Service
配置为NESTED
就不一样了,此时t1
则不会回滚。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
try {
t2Service.func();
} catch (Exception e) {
e.printStackTrace();
}
}
// t2Service
@Transactional(propagation = Propagation.NESTED)
public void func() {
testMapper.updateT2();
int i = 1 / 0;
}
NESTED与REQUIRES_NEW的区别
接下来再来看看NESTED
和REQUIRES_NEW
的区别。
我们分别给t1Service
和t2Service
加上一段System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
用来查看一下当前执行的事务。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
try {
t2Service.func();
} catch (Exception e) {
e.printStackTrace();
}
}
// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
testMapper.updateT2();
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
int i = 1 / 0;
}
输出结果
java
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T2Service.func
如果把REQUIRES_NEW
替换为NESTED
则可以看出实际上还是同一个事务。
java
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T1Service.func
也就是说,使用NESTED
时,虽然还是同一个事务,但却可以在多个方法中进行控制。
实现原理
两个方法同一个事务没有一起回滚,实际上利用的是savepoint
功能,大概方式如下:
java
-- 主事务
savepoint;
-- 执行主事务代码
-- 子事务
savepoint;
-- 执行子事务代码
-- 子事务提交
commit;
-- 执行主事务代码
-- 主事务提交
commit;
所以,搞清楚实现方式以后,你就会发现如果是在主事务中抛出异常,那么子事务也会被回滚,就像下面这样:t1
、t2
都会回滚。
java
// t1Service
@Transactional
public void func() {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
}
// t2Service
@Transactional(propagation = Propagation.NESTED)
public void func() {
testMapper.updateT2();
}
事务的失效场景
关于事务未生效的问题,也是我们在日常开发中经常会遇到的,这一节总结了一些会导致事务失效的场景,你可以了解一下,以免遇到类似的问题。
异常未抛出
被捕获的异常一定要抛出,否则是不会回滚的。
java
// t1Service
@Transactional
public void func() {
try {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
} catch (Exception e) {
// 异常捕获了,未抛出,导致异常事务不会回滚。
e.printStackTrace();
}
}
// t2Service
@Transactional
public void func() {
testMapper.updateT2();
}
异常与rollback不匹配
@Transactional
默认情况下,只会回滚RuntimeException
和Error
及其子类的异常,如果是受检异常或者其他自定义的不属于其子类的异常是不会回滚事务的。
java
@Transactional
public void func() throws Exception {
try {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
} catch (Exception e) {
// 默认情况下非运行时异常不会回滚
throw new Exception();
}
}
修改方式也很简单,@Transactional
支持通过rollbackFor
指定回滚异常类型,可以直接改成rollbackFor = Exception.class
即可
java
// 改成rollbackFor = Exception.class即可
@Transactional(rollbackFor = Exception.class)
public void func() throws Exception {
try {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
} catch (Exception e) {
throw new Exception();
}
}
方法内部直接调用
func2
方法是由func
调用,虽然func2
方法上加了@Transactional
注解,但事务不会生效,testMapper.updateT2()
执行的方法并不会回滚。
java
public void func() {
testMapper.updateT1();
func2();
}
@Transactional
public void func2() {
testMapper.updateT2();
int i = 1 / 0;
}
可以修改为直接通过注入的方式调用即可。
java
@Service
public class T1Service {
@Resource
private TestMapper testMapper;
// 注入T1Service对象
@Resource
private T1Service t1Service;
public void func() {
testMapper.updateT1();
// 通过注入的方式调用
t1Service.func2();
}
@Transactional
public void func2() {
testMapper.updateT2();
int i = 1 / 0;
}
}
注意:SpringBoot 2.6.0版本开始,默认禁止循环依赖,所以如果你使用的版本是2.6.0之后的,那么启动会遇到如下报错。
java
As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
修改方式:在配置文件中把允许循环依赖打开即可。
java
spring.main.allow-circular-references=true
当然,你也可以直接使用AopContext
的方式,像下面这样:
java
public void func() {
testMapper.updateT1();
T1Service t1Service = (T1Service) AopContext.currentProxy();
t1Service.func2();
}
@Transactional
public void func2() {
testMapper.updateT2();
int i = 1 / 0;
}
在另一个线程中使用事务
Spring
事务管理的方式就是通过ThreadLocal
把数据库连接与当前线程绑定,如果新开启一个线程自然就不是一个数据库连接了,自然也就不是一个事务。
t2Service.func()
方法操作的数据并不会被回滚。
java
@Transactional
public void func() {
testMapper.updateT1();
new Thread(() -> t2Service.func()).start();
int i = 1 / 0;
}
注解作用到private级别的方法上
当你写成如下这样时,IDEA
直接会给出提示Methods annotated with '@Transactional' must be overridable
原因很简单,private
修饰的方式,spring
无法为其生成代理。
java
public void func() {
t1Service.func2();
}
@Transactional
private void func2() {
testMapper.updateT1();
int i = 1 / 0;
}
final类型的方法
这个与private
道理是一样的,都是影响了Spring
生成代理对象,同样IDEA
也会有相关提示。
数据库存储引擎不支持事务
注意,如果你使用的是MySQL
数据库,那么常用的存储引擎中只有InnoDB
才支持事务,像MyISAM
是不支持事务的,其他存储引擎都是针对特定场景下使用的,一般也不会用到,不做讨论。
事务的使用建议
Spring
提供了两种使用事务的方式,一种是声明式事务、一种是编程式事务,无论是哪种方式使用起来都非常简单,无需过多介绍,本节主要是针对一些使用不当的场景进行说明。
声明式事务的应用级别
在实际生产应用中是不建议在类似Service
这样的class
类上直接加上@Transactional
注解的,因为这样会导致这个类中的所有方法在执行时都带上了事务,这样原来根本就不需要事务的方法则多了一份负担。
java
@Service
@Transactional // 不要加在class级别
public class DemoService {
}
长事务、过早起开事务
简单来说,就是在整个方法的生命周期内,真正需要事务管理的方法可能只占用了20%
的时间,而其他业务流程占用了80%
的时间,但是由于事务是对整个方法生效,从而导致事务的整体执行时间与整个方法的执行时间一样。
java
@Transactional
public void func() {
// 两个select花费了2秒
select1();
select2();
// 两个save只花费了200毫秒
save1();
save2();
}
解决方式也很简单,把长事务拆分为短事务即可。
java
public void func() {
select1();
select2();
manager.save();
}
@Transactional
public void save() {
save1();
save2();
}
也可以直接使用编程式事务,编程式事务可以更灵活的控制事务的范围。
java
@Resource
private TransactionTemplate transactionTemplate;
public void func() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
testMapper.updateT1();
t2Service.func();
int i = 1 / 0;
}
});
}
事务锁的问题
这和前面的事务执行时间过长是有一定关系的,过长时间的事务,更容易因为锁资源的争抢而导致服务性能下降,同时还需要注意死锁的问题。
事务的整体架构展示
前面提到过,在Spring
中,我们可以直接使用TransactionTemplate
或者PlatformTransactionManager
来操作事务,TransactionTemplate
封装了事务处理的核心流程,使用者只需直接注入即可直接使用,而PlatformTransactionManager
则是更底层的接口,虽然也可以直接通过它来控制事务,但它并不是主流的使用方式,一般还是建议优先使用TransactionTemplate
。
接下来,本小节主要会对Spring
事务的整体架构进行梳理,通过源码的分析,让使用者能够更清楚事务的内部实现原理。
类的关系图
TransactionManager接口
标识性接口
java
public interface TransactionManager {
}
PlatformTransactionManager接口
PlatformTransactionManager
接口继承了TransactionManager
,定义了事务的核心方法,提交和回滚,正如前面提到的,可以直接使用它来控制事务,但并不建议这样做,正确的做法应该是继承AbstractPlatformTransactionManager
类,典型的样例就是JtaTransactionManager
和DataSourceTransactionManager
java
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
AbstractPlatformTransactionManager抽象类
这是一个典型的采用模板方法设计的抽象类,定义了关于事务的核心处理流程。
下图展示了几个关键的抽象方法,其具体的逻辑处理都在子类中。
下图是DataSourceTransactionManager
子类重写的doCommit
和doRollback
方法。
TransactionTemplate
TransactionTemplate
是具体执行事务的入口,XXXTemplate
结尾的类,通常目的都是为了简化使用者处理的流程,这种命名方式也是Spring
的习惯。
TransactionTemplate
中定义的入口方法就是execute
入参TransactionCallback
也是一个标记性接口。
java
@FunctionalInterface
public interface TransactionCallback<T> {
@Nullable
T doInTransaction(TransactionStatus status);
}
java
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
// 方法执行的第一步就需要先确保transactionManager对象不为空,还记得transactionManager吗?就是前面分析的定义了事务处理的关键方法和核心流程的类。
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
而TransactionManager
会作为TransactionTemplate
中的一个属性来使用。
整个execute
中几个关键的方法实际上都是在调用TransactionManager
中提供的方法,doInTransaction
则是执行业务代码的地方。
TransactionDefinition
TransactionDefinition
这个接口主要用来定义事务的传播行为、隔离级别、超时时间、是否只读等属性。
总结
Spring
为我们提供的TransactionTemplate
类,定义了事务处理的基本流程,对外暴露一个execute
方法,并通过传入一个标记性接口TransactionCallback
来实现使用者的业务逻辑处理,大大简化了使用方式。
而PlatformTransactionManager
则更加的灵活,它的目的主要是为了能够让使用者更加方便的在事务流程前后进行业务扩展,比如它的实现类有:HibernateTransactionManager、JpaTransactionManager、JtaTransactionManager
,很明显就是针对不同的ORM
框架而定制的。
所以定义TransactionTemplate
是为了让事务使用更加方便,定义PlatformTransactionManager
是为了让扩展更加的方便。
事务的提交与回滚
最后,我们再来看看事务的提交与回滚的实现逻辑。
commit方法
commit
方法是在AbstractPlatformTransactionManager
类中定义的,是事务的提交的入口方法,具体的处理逻辑主要又由processCommit
和processRollback
这两个方法完成。
java
@Override
public final void commit(TransactionStatus status) throws TransactionException {
// 判断事务是否已经完成
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
processCommit(defStatus);
}
processCommit
该方法执行了很多扩展方法,以及一些与事务传播类型有关的逻辑处理,最终的事务处理又由doCommit
方法来实现。
java
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
// 这是一个空方法,主要是交由具体的事务处理器来实现
prepareForCommit(status);
// 下面两个方法都是Spring留出的扩展点,通过TransactionSynchronizationManager提供的registerSynchronization方法,可以注册TransactionSynchronization实例,从而调用TransactionSynchronization提供的相关方法
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 嵌套事务的处理分支
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
// 大多数都是一个新的事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 同样的具体交给事务处理器来完成
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
// triggerAfterCommit和triggerAfterCompletion,和前面的triggerBeforeCommit、triggerBeforeCompletion两个方法一样,都是Spring留下的扩展点
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
doCommit
方法执行到了这一步,实际上就是获取Connection
,然后调用commit
即可,这是JDBC
的使用标准。
java
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
processRollback
接下来是回滚,与事务的提交处理其实差不多,可以自行阅读。
java
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}