ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring事务原理源码简述

2022-08-05 11:01:14  阅读:205  来源: 互联网

标签:事务 Spring connection getConnectionHolder txObject 简述 源码 oldValue con


spring框架的事务其实也是基于jdbc的事务,而一般的jdbc事务代码如下形式


        try{
            connection.setAutoCommit(false);//turn off autocommit transaction
            ...数据操作
            connection.commit();//commit transaction by self
        }
        catch (Exception e){
            connection.rollback();
            e.printStackTrace();
        }

如果每次数据库操作都按照这种原生的方式来写,那么代码里面会多出非常多冗余代码,因此spring框架利用自身aop机制,为使用者提供了@transactional注解,实现注解式事务管理
spring的事务其实也是基于上面原生jdbc来做的,因此事务的管理本质上还是对jdbc connection的管理,核心的类和方法如下:

AbstractPlatformTransactionManager 看名字知道这是一个定义基本方法和流程的抽象类,它串联了一个事务的基本操作,包括提交回滚等,可以看作是上面原生jdbc的抽象

DataSourceTransactionManager doBegin() 这是执行事务操作的地方,代码如下,和原生jdbc的connection.setAutoCommit(false)对应,但其中还有一步关键步骤,获取连接

@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
                //DataSourceTransactionObject是一个包装类,其中有各种事务的属性和连接包装类
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                                //获取连接,包装在ConnectionHolder中
				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).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}

			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);
		}
	}

这个方法中获取连接是关键,看看spring是如何实现不同请求事务的隔离的,Connection newCon = obtainDataSource().getConnection();这一句获取了一个数据库连接,而下面有这样一句,还带着注释

// Bind the connection holder to the thread.
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}

注释翻译过来就是把connection和线程绑定起来,显然就是不同事务隔离的关键,点进去看看:

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");
...
public static void bindResource(Object key, Object value) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Assert.notNull(value, "Value must not be null");
		Map<Object, Object> map = resources.get();
		// set ThreadLocal Map if none found
		if (map == null) {
			map = new HashMap<>();
			resources.set(map);
		}
		Object oldValue = map.put(actualKey, value);
		// Transparently suppress a ResourceHolder that was marked as void...
		if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
			oldValue = null;
		}
		if (oldValue != null) {
			throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
					actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
		}
		 
	}

resources这个变量是关键,他是一个NamedThreadLocal,其实就是ThreadLocal加个名字,正是ThreadLocal实现了connection和线程的对应绑定,要使用时候调用doGetResource(Object actualKey) 方法获取即可

标签:事务,Spring,connection,getConnectionHolder,txObject,简述,源码,oldValue,con
来源: https://www.cnblogs.com/CodeSpike/p/16553611.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有