中小型企业网站选择什么配置的亚马逊服务器,网站设计优化方案,搭建网站服务器多少钱,wordpress注册登录弹窗代码前言
本文主要介绍的是在Spring框架中有关事务的应用方式#xff0c;以及一些生产中常见的与事务相关的问题、使用建议等。同时#xff0c;为了让读者能够更容易理解#xff0c;本文在讲解过程中也会通过源码以及案例等方式进行辅助说明#xff0c;通过阅读本文不但能够解…前言
本文主要介绍的是在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嵌套方式使用前一个方法的事务创建一个新的事务是
如果你还不清楚这些传播类型具体有什么区别也没关系接下来本节会针对每一种类型分别进行效果演示以此来帮助你进行理解。
在此之前先准备一下演示的数据
CREATE TABLE t1 (id int(11) NOT NULL,c int(11) DEFAULT NULL,PRIMARY KEY (id),KEY idx_c (c)
) ENGINEInnoDB DEFAULT CHARSETutf8;CREATE TABLE t2 (id int(11) NOT NULL,c int(11) DEFAULT NULL,PRIMARY KEY (id),KEY idx (c)
) ENGINEInnoDB DEFAULT CHARSETutf8;两条更新语句分别对t1、t2两张表进行更新。
update idupdateT1update t1 set c 2 where id 1
/update
update idupdateT2update t2 set c 2 where id 1
/update
REQUIRED
含义支持当前事务如果当前不存在则新开启一个事务默认配置。
下面这块代码逻辑为当调用T1Service中的func方法时除了要更新t1表数据之外还会调用t2Service中的func方法更新t2表不过在执行t2Service中的方法时会遇到异常。 Service
public class T1Service {Resourceprivate TestMapper testMapper;Resourceprivate T2Service t2Service;Transactionalpublic void func() {testMapper.updateT1();t2Service.func();}
}Service
public class T2Service {Resourceprivate TestMapper testMapper;Transactionalpublic 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两张表数据都不会回滚。
Service
public class T1Service {Resourceprivate TestMapper testMapper;Resourceprivate T2Service t2Service;// 去掉事务 Transactionalpublic void func() {testMapper.updateT1();t2Service.func();}
}Service
public class T2Service {Resourceprivate 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)
// t1Service
public void func() {testMapper.updateT1();t2Service.func();
}// t2Service
Transactional(propagation Propagation.MANDATORY)
public void func() {testMapper.updateT2();int i 1 / 0;
}异常信息
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation mandatoryat 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的数据不会被更新。
// 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中。
// 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的数据。
// 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
含义以非事务方式执行如果当前已存在事务则抛出异常。
如字面含义直接抛出异常。
// t1Service
Transactional
public void func() {testMapper.updateT1();t2Service.func();
}// t2Service
Transactional(propagation Propagation.NEVER)
public void func() {testMapper.updateT2();
}org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation neverat 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自己抛出异常后数据也是不会回滚的完全是当做无事务处理。
// 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抛出的异常进行了捕获并且自己也没有再抛出。 // 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则不会回滚。
// 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());用来查看一下当前执行的事务。
// 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;
}输出结果
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T2Service.func如果把REQUIRES_NEW替换为NESTED则可以看出实际上还是同一个事务。
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T1Service.func也就是说使用NESTED时虽然还是同一个事务但却可以在多个方法中进行控制。
实现原理
两个方法同一个事务没有一起回滚实际上利用的是savepoint功能大概方式如下
-- 主事务
savepoint;
-- 执行主事务代码-- 子事务
savepoint;-- 执行子事务代码-- 子事务提交
commit;-- 执行主事务代码-- 主事务提交
commit;所以搞清楚实现方式以后你就会发现如果是在主事务中抛出异常那么子事务也会被回滚就像下面这样t1、t2都会回滚。
// t1Service
Transactional
public void func() {testMapper.updateT1();t2Service.func();int i 1 / 0;
}
// t2Service
Transactional(propagation Propagation.NESTED)
public void func() {testMapper.updateT2();
}事务的失效场景
关于事务未生效的问题也是我们在日常开发中经常会遇到的这一节总结了一些会导致事务失效的场景你可以了解一下以免遇到类似的问题。
异常未抛出
被捕获的异常一定要抛出否则是不会回滚的。
// 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及其子类的异常如果是受检异常或者其他自定义的不属于其子类的异常是不会回滚事务的。
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即可
// 改成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()执行的方法并不会回滚。
public void func() {testMapper.updateT1();func2();
}Transactional
public void func2() {testMapper.updateT2();int i 1 / 0;
}可以修改为直接通过注入的方式调用即可。
Service
public class T1Service {Resourceprivate TestMapper testMapper;// 注入T1Service对象Resourceprivate T1Service t1Service;public void func() {testMapper.updateT1();// 通过注入的方式调用t1Service.func2();}Transactionalpublic void func2() {testMapper.updateT2();int i 1 / 0;}
}注意SpringBoot 2.6.0版本开始默认禁止循环依赖所以如果你使用的版本是2.6.0之后的那么启动会遇到如下报错。 As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.修改方式在配置文件中把允许循环依赖打开即可。
spring.main.allow-circular-referencestrue当然你也可以直接使用AopContext的方式像下面这样
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()方法操作的数据并不会被回滚。
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无法为其生成代理。
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注解的因为这样会导致这个类中的所有方法在执行时都带上了事务这样原来根本就不需要事务的方法则多了一份负担。
Service
Transactional // 不要加在class级别
public class DemoService {
}长事务、过早起开事务
简单来说就是在整个方法的生命周期内真正需要事务管理的方法可能只占用了20%的时间而其他业务流程占用了80%的时间但是由于事务是对整个方法生效从而导致事务的整体执行时间与整个方法的执行时间一样。
Transactional
public void func() {// 两个select花费了2秒select1();select2();// 两个save只花费了200毫秒save1();save2();
}解决方式也很简单把长事务拆分为短事务即可。
public void func() {select1();select2();manager.save();
}Transactional
public void save() {save1();save2();
}也可以直接使用编程式事务编程式事务可以更灵活的控制事务的范围。
Resource
private TransactionTemplate transactionTemplate;public void func() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {testMapper.updateT1();t2Service.func();int i 1 / 0;}});
}事务锁的问题
这和前面的事务执行时间过长是有一定关系的过长时间的事务更容易因为锁资源的争抢而导致服务性能下降同时还需要注意死锁的问题。
事务的整体架构展示
前面提到过在Spring中我们可以直接使用TransactionTemplate或者PlatformTransactionManager来操作事务TransactionTemplate封装了事务处理的核心流程使用者只需直接注入即可直接使用而PlatformTransactionManager则是更底层的接口虽然也可以直接通过它来控制事务但它并不是主流的使用方式一般还是建议优先使用TransactionTemplate。
接下来本小节主要会对Spring事务的整体架构进行梳理通过源码的分析让使用者能够更清楚事务的内部实现原理。
类的关系图 TransactionManager接口
标识性接口
public interface TransactionManager {}PlatformTransactionManager接口
PlatformTransactionManager接口继承了TransactionManager定义了事务的核心方法提交和回滚正如前面提到的可以直接使用它来控制事务但并不建议这样做正确的做法应该是继承AbstractPlatformTransactionManager类典型的样例就是JtaTransactionManager和DataSourceTransactionManager
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也是一个标记性接口。
FunctionalInterface
public interface TransactionCallbackT {NullableT doInTransaction(TransactionStatus status);
}Override
Nullable
public T T execute(TransactionCallbackT 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 - rollbackrollbackOnException(status, ex);throw ex;}catch (Throwable ex) {// Transactional code threw unexpected exception - rollbackrollbackOnException(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这两个方法完成。
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方法来实现。
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 didnt 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 doCommittriggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// can only be caused by doCommitif (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的使用标准。
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
接下来是回滚与事务的提交处理其实差不多可以自行阅读。
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 transactionif (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 were asked to fail earlyif (!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 markerif (unexpectedRollback) {throw new UnexpectedRollbackException(Transaction rolled back because it has been marked as rollback-only);}}finally {cleanupAfterCompletion(status);}