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) 方法获取即可