目录
事务基础
四大特性
原子性 | 组成一个事务的多个数据库操作是一个不可分割的原子单位,一个操作成功所有都成功, 一个失败必须都失败 |
一致性 | 事务操作成功后, 数据库所处的状态和它的业务规则是一致的,即数据不会被破坏. |
隔离性 | 并发操作时, 不同的事务拥有各自的数据空间, 相互不会有影响 |
持久性 | 一但事务提交, 事务中操作的所有数据都落入数据库, 即使数据库崩溃, 重启后数据也能恢复. |
并发问题
通过锁机制和隔离级别解决
脏读 | A事务读取了B事务尚未提交的更改数据,并在这个数据基础上做了操作. |
不可重复读 | AB事务同时读取一个数据, B事务先提交, A事务读取了B事务已经提交的数据. 两次读取数据结果不一致. |
幻读 | A事务读取了B事务提交的新增数据, AB两个事务同时读取一个统计数据, B事务新增了一条记录并提交, A做统计时两次结果不一致. |
一类丢失更新 | A事务撤销时, 把已经提交的B事务的更新数据覆盖了.AB两个事务, B事务先提交, A事务发生异常回滚, 将B事务提交的更新数据覆盖了. |
二类丢失更新 | A事务覆盖B事务已经提交的数据, 造成B事务所做操作丢失.AB同时读取一条记录,B事务先提交, A事务提交后覆盖了B事务. |
隔离级别
隔离级别解决的是数据库的并发问题:事务的隔离级别和数据库并发性是对立的, 读未提交具有较高的并发性和吞吐量, 序列化数据库并发性最低. 默认: repeatable read
数据库锁
数据库支持的锁都是悲观锁
行锁 | 表锁 | 记录锁 | 乐观锁 | |
概念 | 行锁是基于索引加的, 就是说索引到几行就会锁定几行, 所以行锁要加载索引响应的行上, 就是命中的索引. | 表锁是非索引字段, 一锁锁一整张表, 必须等当前表的锁被释放后才能进行其他操作; | 记录锁和行锁类似,是在行锁基础上衍生出来的一种锁, 记录锁命中的必须是唯一索引如ID | 乐观锁不是数据库支持的, 需要自己实现, 就是在数据表中增加version字段, 每次操作+1, 当更新时发现跟读取的版本不一致, 需要再次获取. |
特征 | 锁冲突概率低, 并发性高, 但是会有死锁的情况出现 | 锁冲突几率高, 不会出现死锁 | 参考行锁 | 不会出现死锁和线程挂起 |
语法 | set autocommit = 0; select * from t where account = 'xxx' for update; update t set a=1 where account = 'xxx'; commit; | set autocommit = 0; select * from t for update; update t set a=1 where id = 3; commit; | set autocommit = 0; select * from t where ID = 3 for update; update t set a=1 where ID = 3; commit; | update set a = 1, v=v+1, where id = 3 and v=2; |
使用注解进行注解声明
@AliasFor("transactionManager") 和 @AliasFor("value") 声明事务管理器, 标识两种方式作用一样
Propagation propagation() default Propagation.REQUIRED; 指定事务传播特性. 默认REQUIRED
Isolation isolation() default Isolation.DEFAULT; 指定事务隔离级别, 默认数据库
Class<? extends Throwable>[] rollbackFor() default {}; 指定回滚方式为发生异常, 传入指定异常类型.
解决嵌套事务问题
期望: A事务调用B和C两个事务, B或C事务失败以后A事务不会提交; A事务失败以后B和C不会提交.
// 外层事务
@Transactional(rollbackFor = Exception.class)
public Boolean updateStatus() {
TxUser txUser = new TxUser();
txUser.setUserName("lial");
txUser.setId(1L);
boolean userStatus = txUserService.update(txUser);
TxUserProfile txUserProfile = new TxUserProfile();
txUserProfile.setAddress("北京");
txUserProfile.setId(1L);
boolean profileStatus = txUserProfileService.update(txUserProfile);
// 同时更新成功 抛出异常
if(userStatus && profileStatus){
System.out.println(1/0);
}
return false;
}
// txUserService.update(txUser) 内部事务1
@Transactional(rollbackFor = Exception.class)
public boolean update(TxUser txUser) {
return txUserMapper.update(txUser);
}
// txUserProfileService.update(txUserProfile) 内部事务2
@Transactional(rollbackFor = Exception.class)
public boolean update(TxUserProfile txUserProfile) {
return txUserProfileMapper.update(txUserProfile);
}
运行日志:
o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'transactionManager'
o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [com.learn.tx.service.impl.TxDemoServiceImpl.updateStatus]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
执行第一个sql
o.s.j.d.DataSourceTransactionManager : Participating in existing transaction
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528]
执行sql语句
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528]
执行第二个sql开始
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528] from current transaction
执行sql语句
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c9f4376]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c9f4376]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3c9f4376]
o.s.j.d.DataSourceTransactionManager : Initiating transaction commit
o.s.j.d.DataSourceTransactionManager : Committing JDBC transaction on Connection [HikariProxyConnection@308976189 wrapping com.mysql.cj.jdbc.ConnectionImpl@6f6c8d45]
o.s.j.d.DataSourceTransactionManager : Releasing JDBC Connection [HikariProxyConnection@308976189 wrapping com.mysql.cj.jdbc.ConnectionImpl@6f6c8d45] after transaction
o.s.jdbc.datasource.DataSourceUtils : Returning JDBC Connection to DataSource
默认情况下, @Transactional是可以解决事务嵌套问题的, @Transactional的默认传播特性为PROPAGATION_REQUIRED, 支持当前事务, 如果不存在创建一个新的事务.
public enum Propagation {
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
}
日志分析如下:
一. Creating new transaction with name [com.learn.tx.service.impl.TxDemoServiceImpl.updateStatus]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
利用DataSourceTransactionManager创建了一个新事务, 传播特性为PROPAGATION_REQUIRED, 隔离级别ISOLATION_DEFAULT.
二. 执行第一个sql
Participating in existing transaction : 加入一个已存在的事务当中
Creating a new SqlSession: 会话资源编号: 2ecdf528
执行sql语句
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528]: 2ecdf528会话资源编号被释放
三. 执行第二个sql
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528] from current transaction: 从当前外部事务中拉取到了事务编号2ecdf528
执行sql语句
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdf528]: 2ecdf528会话资源编号被释放
四. 事务提交并关闭
事务源码分析
本次源码分析主要看事务的初始化过程, 执行过程, 生命周期
事务关键类介绍
TransactionInterceptor
注解相当于标记, 单纯注解不会做任何事, 需要有与注解对应的拦截处理, 在spring-tx中, 担任@Transactional注解拦截器的类是TransactionInterceptor, 以下是这个类的结构图:
AbstractPlatformTransactionManager
而在事务的具体执行过程中, 如提交, 回滚挂起, 又出现了另外一套关键的抽像模板类AbstractPlatformTransactionManager, 这个类可以理解为事务执行动作的模板抽象, 提供了部分事务动作的基础实现, 同时将核心逻辑全部交由子类做具体实现, 最大限度提升了事务执行的拓展性和复用性, 以下为该类的结构图;
事务处理模板
在TransactionInterceptor仅做了一个拦截调用, 具体实现全在TransactionAspectSupport的抽象父类当中, 这个抽象类贯穿事务执行的始终. 以下是invoke代码,
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
做了两件事: 1, 获取AOP增强的目标类, 2, 调用父类invokeWithinTransaction进行事务处理. 以下为invokeWithinTransaction关键代码, 有删减:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
// 1, 事务属性资源配置
TransactionAttributeSource tas = getTransactionAttributeSource();
// 2, 事务属性定义用当前事务配置的隔离级别, 传播特性等于spring自身定义关联
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 3, 确定事务管理器用来处理当前事务
final TransactionManager tm = determineTransactionManager(txAttr);
// 4, 平台式事务管理PlatformTransactionManager用来执行具体事务动作
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 5, 获取函数的描述符 com.xx.xxx.class.method()
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 6, 事务提交和回滚标准化, 同时数数据源+spring事务管理都在这里处理
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 7, 调用业务代码
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 8, 事务发生异常后的处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 9,清除事务状态信息
cleanupTransactionInfo(txInfo);
}
// 9, 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
构建TransactionInfo事务标准化
第6步重点看下TransactionInfo的构建过程,
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
// 1, 确定事务名称, 如果没有指定事务名, 默认用方法名作为事务名
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 2, 构建事务核心信息并返回当前事务的执行状态
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 3, 将事务属性和状态进行标准化构建
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
到这里 已经找到了初始化事务的主要工作 重点看第2步, tm.getTransaction(txAttr); PlatformTransactionManager接口提供了事务的具体执行动作, 提交/回滚以及getTransaction, 在这个接口中有一个关键的抽象实现AbstractPlatformTransactionManager, 这个抽象类的核心作用就是提供了统一的事务初始化模板, 同时预留了很多钩子, 或者说连接点, 例如: begin
,suspend
, resume
,commit
,rollback
等, 这些方法全部交给子类做具体实现, 以下就是getTransaction的模板实现
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
// 1, 事务定义设置, 如果没指定事务的配置信息, 将使用默认配置
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 2, 获取事务对象
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 3, 基础验证 如果存在事务已已有事务执行,
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(def, transaction, debugEnabled);
}
// Check definition settings for new transaction.
// 4, 基础校验超时时间配置
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 5, 如果事务的传播特性为PROPAGATION_MANDATORY, 直接抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 挂起给定的事务。首先暂停事务同步,然后委托给{@code doSuspend}模板方法。
// 6, 实际就是让子类自己实现, 触发相关的挂起注册的事件
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
// 正式开始创建事务
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 7, 创建一个新的事务状态 就是new DefaultTransactionStatus() 把个属性都赋值上
DefaultTransactionStatus status = newTransactionStatus(
def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 8, 事务实例华开始
doBegin(transaction, def);
// 9, 初始化和同步事务状态
prepareSynchronization(status, def);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// PROPAGATION_SUPPORTS PROPAGATION_NEVER PROPAGATION_NOT_SUPPORTED
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
doBegin(transaction, def);需要仔细分析一下, 这里是核心代码, 在看之前思考一个问题:为啥数据库设置的是自动提交 ,但是用spring事务执行的时候,还是一起提交到数据库的?
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 获取数据源, 前面getTransaction挂起事务的时候指定ConnectionHolder为null
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 获取数据源, 日志里打印的那一堆就从这里来的
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 标记为同步事务
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 重新获取刚刚装配的数据源
con = txObject.getConnectionHolder().getConnection();
// 指定隔离级别只读事务等
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
// 这里非常的关键,先看看Connection 是否是自动提交的
// 如果是 就con.setAutoCommit(false) 要不然数据库默认没执行一条SQL都是一个事务,就没法进行事务的管理了
// 所以从这个判断完成以后开始, 当前事务中所有的sql都不会自动提交, 全部交给spring做手动提交. 从而实现事务管理.
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 如果仅仅只是查询。把事务的属性设置为readonly=true Spring会帮忙对SQl进行优化
// 标记为只读事务就只能查询到事务执行前的数据, 查不到后续的数据
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// 设置事务的超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
// 把当前的链接 和当前的线程进行绑定
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
到这里事务的装配全部完成, 通过invokeWithinTransaction函数中的retVal = invocation.proceedWithInvocation();调用我们的业务代码, 调用本地业务代码会产生两种结果, 成功或失败, 无论成功还是失败都会清除事务状态信息, 恢复事务上下文. 成功提交事务, 失败回滚事务; 重点看下回滚和提交;
事务回滚
事务回滚(completeTransactionAfterThrowing)
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 1, 验证当前异常是否是调用方指定的异常
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 2, 执行回滚操作
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
// 如果TransactionStatus.isRollbackOnly() is true还是会回滚, 如果不是true直接提交了...
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
在第二部执行回滚操作的时候, 获取当前事务管理器进行回滚操作,回滚代码如下
public final void rollback(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;
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 1, 触发回滚完成前操作, 清理事务
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);
}
}
在执行doRollback(status);操作的时候, 是由AbstractPlatformTransactionManager的子类做具体实现, 项目中用的ibatis所以他的实现类是DataSourceTransactionManager, 回滚的时候获取java.sql.Connection数据源链接操作的回滚.
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
事务提交
以下代码 原理通回滚操作
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
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);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
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(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
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 new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
归纳
事务的执行过程
事务初始化过程
核心接口
在事务管理周期中, 有三个关键接口分别是TransactionDefinition,PlatformTransactionManager,TransactionStatus.
TransactionDefinition
描述事务的隔离级别, 超时时间, 是否为只读事务和事务的传播规则等控制事务具体行为的事务属性
PlatformTransactionManager
根据TransactionDefinition提供的事务属性, 创建事务并调用TransactionStatus激活事务,这是Spring事务基础结构中的中心接口。
应用程序可以直接使用它,但它主要不是作为API, 通常,应用程序将使用TransactionTemplate或通过AOP进行声明性事务界定。
建议从所提供的派生org.springframework.transaction.support.AbstractPlatformTransactionManager
那些预先实现了定义的传播行为并负责事务同步处理的子类必须实现, 用于基础事务的特定状态的模板方法,例如:开始、暂停、恢复、提交
这个策略接口的默认实现是org.springframe.transaction.jta.JtaTransactionManager和org.springframework.jdbc.datasource.DataSourceTransactionManager可以作为其他事务策略的实现指南。
关系:
继承自: TransactionManager
方法:
// 将事务回滚, 当commit()调用发生异常时, rollback()会被隐式调用.
void rollback(TransactionStatus status) throws TransactionException;
//根据事务的状态返回提交事务, 如果事务状态已经被表示为rollback-only, 该方法将执行一个回滚事务的操作.
void commit(TransactionStatus status) throws TransactionException;
//根据事务定义信息从事务环境中返回一个已经存在的事务, 或者创建一个新的事务, 并用TransactionStatus描述这个事务的状态.
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
TransactionStatus
关系:
TransactionExecution, SavepointManager, Flushable
方法:
// 返回该事务是否在内部带有保存点,也就是说,已创建为基于保存点的嵌套事务,此方法主要用于诊断, 用于自定义保存点的编程处理, 使用SavepointManager提供的操作
boolean hasSavepoint();
//刷新潜在的会话进行存储,例如hibernate会话
void flush();