Spring 事务 状态信息的创建、回滚、清理、提交
为什么80%的码农都做不了架构师?>>>
Spring 的事务是依据AOP来实现的,通过AOP从切面来抓捕异常判断是否回滚以及如何回滚,若是正常的返回则是清理事务信息,实施事务提交动作,具体的代码入口在 TransactionInterceptor 里面 的 invoke 方法,
@Overridepublic Object invoke(final 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, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});}
具体的实现细节在 invokeWithinTransaction 里面
/*** General delegate for around-advice-based subclasses, delegating to several other template* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}* as well as regular {@link PlatformTransactionManager} implementations.* @param method the Method being invoked* @param targetClass the target class that we're invoking the method on* @param invocation the callback to use for proceeding with the target invocation* @return the return value of the method, if any* @throws Throwable propagated from the target invocation*/protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// If the transaction attribute is null, the method is non-transactional.final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}else {// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.try {Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus status) {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.return new ThrowableHolder(ex);}}finally {cleanupTransactionInfo(txInfo);}}});// Check result: It might indicate a Throwable to rethrow.if (result instanceof ThrowableHolder) {throw ((ThrowableHolder) result).getThrowable();}else {return result;}}catch (ThrowableHolderException ex) {throw ex.getCause();}}}
基本的实现流程如上面阐述的三个阶段, 创建事务信息 , 方式异常就回滚, 清理事务信息以及提交事务。
事务状态信息的创建
事务状态信息的创建比较复杂,分为两点来讲, 一点是 :当前已存在事务如何处理,另外就是 不存在事务的处理方式。
已存在事务:
已存在的事务创建里面当发现当前事务正在运行,就保存当前事务的节点信息,以供后续异常回滚时候按照当前节点进行回滚的需要;
if (useSavepointForNestedTransaction()) {// Create savepoint within existing Spring-managed transaction,// through the SavepointManager API implemented by TransactionStatus.// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);status.createAndHoldSavepoint();return status;}
另外还有一点在这个流程里面比较重要的是:事务的挂起,当需要创建一个新的事务的时候就需要将当前的事务挂起,挂起的动作其实就是从当前维护秩序 ThreadLocal 里面去除执行事务并在创建当前事务的时候把去除的事务信息挂载进去,当执行完当前事务再提取出来执行
public void suspend() { if (this.holderActive) { TransactionSynchronizationManager.unbindResource(this.resourceKey); }
}............if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } catch (Error beginErr) { resumeAfterBeginException(transaction, suspendedResources, beginErr); throw beginErr; } }
调用 suspend 方法挂起当前事务,并退出当前事务资源,保存在新的事务里面。当后续提交或者回滚的时候,代码会判断当前是否有挂起事务,有挂起事务就恢复到当前线程
private void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {if (status.isDebug()) {logger.debug("Resuming suspended transaction after completion of inner transaction");}resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());}
不存在已经有的事务
事务回滚
事务回滚由具体的db客户端实现类实现,如下他们的继承关系如下:
具体的回滚代码以 DataSourceTransactionManager 为例:
protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();if(status.isDebug()) {this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");}try {con.rollback();} catch (SQLException var5) {throw new TransactionSystemException("Could not roll back JDBC transaction", var5);}}
就是获取当前事务的链接,执行回滚,这个链接在创建事务信息的时候已经说明。具体到con.rollback() 可以以 MySQL的连接客户端代码为例,即通过链接 io 发送 rollback的命令到服务器来实现回滚,如下
private void rollbackNoChecks() throws SQLException {if ((getUseLocalTransactionState()) && (versionMeetsMinimum(5, 0, 0)) && (!this.io.inTransactionOnServer())) {return;}execSQL(null, "rollback", -1, null, 1003, 1007, false, this.database, null, false);}
一个 execSQL 就通过链接io向服务器提交sql语句的过程。
事务清理
事务清理,比较简单,如下代码:
protected void cleanupTransactionInfo(TransactionInfo txInfo) {if (txInfo != null) {txInfo.restoreThreadLocalStatus();}}private void restoreThreadLocalStatus() {// Use stack to restore old transaction TransactionInfo.// Will be null if none was set.transactionInfoHolder.set(this.oldTransactionInfo);}
只是将事务信息里面 TransactionInfoHolder 重新设置为 oldTransactionInfo 而已。
事务提交
事务提交完成最后有一段方法是cleanupAfterCompletion,这个代码如下:
private void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {if (status.isDebug()) {logger.debug("Resuming suspended transaction after completion of inner transaction");}resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());}}
在之前的事务状态信息创建的时候也提到,resume是对挂起事务的还原,具体的resume如下:
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)throws TransactionException {if (resourcesHolder != null) {Object suspendedResources = resourcesHolder.suspendedResources;if (suspendedResources != null) {doResume(transaction
, suspendedResources);}List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;if (suspendedSynchronizations != null) {TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);doResumeSynchronization(suspendedSynchronizations);}}}
主要是将挂起资源信息取出还原到当前同步线程中,并且把整个事务资源信息注册进去,如下:
public static void registerSynchronization(TransactionSynchronization synchronization)throws IllegalStateException {Assert.notNull(synchronization, "TransactionSynchronization must not be null");if (!isSynchronizationActive()) {throw new IllegalStateException("Transaction synchronization is not active");}synchronizations.get().add(synchronization);}
转载于:https://my.oschina.net/exit/blog/814612
Spring 事务 状态信息的创建、回滚、清理、提交相关推荐
- java事务什么时候回滚_spring事务什么时候会自动回滚
在java中异常的基类为Throwable,他有两个子类xception与Errors.同时RuntimeException就是Exception的子类,只有RuntimeException才会进行回 ...
- 0046 @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚
0046 @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚 参考文章: (1)0046 @Transactional注解的几个参数--事务传播控制--事务隔离级 ...
- git revert回滚merge提交时报错(commit xxx is a merge but no -m option )
git revert 回滚merge提交 报错内容: $ git revert 788bab4 error: commit 788bab4021fe14db3a0865aa55fd1b798b74c2 ...
- SVN 撤回(回滚)提交的代码
转: SVN 撤回(回滚)提交的代码 2016年12月20日 17:20:58 怀色 阅读数 68614 标签: svn svn回滚 版本回滚 更多 个人分类: svn 版权声明:本文为博主原创文章, ...
- Spring.Net使用事务,try catch不回滚
使用Spring.Net的事务管理,当事务执行方法中出现异常是,若不添加try catch即抛出异常时,事务起作用,数据操作回滚,但程序终止. 当使用try catch捕获异常后,事务不起作用, ...
- Java中Spring中的方法加上try catch后事务管理器失效无法回滚的情况
beab.xml配置 <bean id="dataSource" class="org.springframework.jdbc.datasource.Driver ...
- Oracle数据库事务回滚和提交,数据库 事务提交和回滚
事务 - (transaction / tx) 原子性操作性(不可以分割的操作) - 要么全做, 要么全不做 事务的特点 - ACID 特性 A - atomicity 原子性 : 不可分割 ...
- sql 事务提交和回滚_SQL提交和回滚
sql 事务提交和回滚 The most important aspect of a database is the ability to store data and the ability to ...
- @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚
@Transactianal注解有一些属性,见Spring-Framework-Reference Table17.3 Transactional-settings @Transactional(pr ...
最新文章
- php示例代码使用mysql_fetch_assoc函数
- 分析大咖说:做一个转化率提升 2% 的成功官网改版案例
- 【金三银四】启动mysql服务器
- 印度软件开发人员_我如何辍学并在19岁时在印度找到了一份开发人员的工作
- leetcode:Majority Number
- hdu 4006 The kth great number (优先队列)
- 解决 Error L6915E 问题
- php mysql查询中文乱码_解决php mysql查询插入中文乱码问题_PHP教程
- 【Spring 5】响应式Web框架实战(上) 1
- 如何用 Python 爬取需要登录的网站
- LAN9252采用外部阻容复位的时候,RESET引脚一直为低的原因以及对应解决办法。
- 入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???
- 好看的网站发布导航页HTML源码
- 百度地图标记打点展示
- cuda官网下载,本机显卡驱动与cuda、cudnn、pytorch、torchvision安装版本对应表,以及完全卸载CUDA方法
- 做题遇到的trick和想法
- 极好的搜索引擎: Goolgle 本网站和www搜索插件
- NGO招志愿者翻译,请分享
- Copyright 与 Copyleft
- 普通的加载千篇一律,有趣的 loading 万里挑一