Mybatis源码分析——事务

本文深入探讨Mybatis中的事务管理,重点关注SpringManagedTransactionFactory和SpringManagedTransaction。当与Spring集成时,Mybatis默认使用SpringManagedTransactionFactory创建事务。TransactionFactory的实现包括JdbcTransactionFactory、ManagedTransactionFactory和SpringManagedTransactionFactory,而Transaction的实现对应其Factory。在执行SQL时,通过SpringManagedTransaction获取和管理数据库连接,涉及事务的开启、提交、回滚和关闭。事务管理器DataSourceTransactionManager在Spring中负责事务的控制和处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们平时使用db都离不开事务,那在mybatis中是如何实现事务的呢。

mybatis中有事务Transaction,必有生产事务的TransactionFactory。

TransactionFactory:有3个实现类,分别是JdbcTransactionFactory、ManagedTransactionFactory和SpringManagedTransactionFactory,前2者是mybatsi自带的,第3者则是mybatis-spring包中的。前2者都比较简单,本章重点介绍SpringManagedTransactionFactory。

Transaction:也同样有3个实现类,跟上面的Factory相对应,分别是JdbcTransaction、ManagedTransaction和SpringManagedTransaction。

 

我们接着看,在spring集成mybatis中是如何使用事务的。在解析mybatis-config.xml配置文件的时候,如果没有指定事务工厂,就默认使用SpringManagedTransactionFactory,如下

    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

然后在DefaultSqlSessionFactory.openSession时获取事务工厂,生产事务并绑定到Executor,如下

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }
  @Override
  public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
    return new SpringManagedTransaction(dataSource);
  }

可以看到,调用事务工厂的newTransaction时,返回了SpringManagedTransaction实例。

 

那又是何时通过事务获取数据库连接的呢,答案是在Executor获取Statement的时候,SimpleExecutor的prepareStatement,如下

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

这里调用的getConnection,会调用它的父类BaseExecutor的getConnection,如下

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

然后在这里就调用Transaction获取Connection。

 

好了,我们再来看看Transaction都有哪些方法吧!SpringManagedTransaction继承于Transaction,有5个方法,如下

/**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * Get transaction timeout if set
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;

我们一个一个来看吧,先看getConnection,如下

  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }
  private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
  }

这里是通过spring的一个工具类来获取connection,如下

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}
	}

真正干活的是下面do打头的doGetConnection,如下

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(dataSource.getConnection());
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = dataSource.getConnection();

		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			logger.debug("Registering transaction synchronization for JDBC Connection");
			// Use same Connection for further JDBC actions within the transaction.
			// Thread-bound object will get removed by synchronization at transaction completion.
			ConnectionHolder holderToUse = conHolder;
			if (holderToUse == null) {
				holderToUse = new ConnectionHolder(con);
			}
			else {
				holderToUse.setConnection(con);
			}
			holderToUse.requested();
			TransactionSynchronizationManager.registerSynchronization(
					new ConnectionSynchronization(holderToUse, dataSource));
			holderToUse.setSynchronizedWithTransaction(true);
			if (holderToUse != conHolder) {
				TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
			}
		}

		return con;
	}

这里从事务管理器中获取ConnectionHolder,然后从Holder中获取Connection,如果没有,就直接从DataSource里获取,当然这里的DataSource其实就是我们的线程池的实现类了,关于线程池,留到后面讲,这里不展开。

如果获取到的ConnectionHolder是空的,就会新建一个Holder,并把它注册到事务管理器中。这里的TransactionSynchronizationManager是做了同步锁处理的。

回到openConnection()方法,isConnectionTransactional是标记连接是否在事务管理器中。

接着看commit()方法,如下

  @Override
  public void commit() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Committing JDBC Connection [" + this.connection + "]");
      }
      this.connection.commit();
    }
  }

这里会判断,如果不受事务管理器管理,并且不是自动提交,就会执行connection的commit提交。如果是在事务管理器中的话,就是在事务管理器的commit里做处理。

再来看rollback()方法,如下

  @Override
  public void rollback() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Rolling back JDBC Connection [" + this.connection + "]");
      }
      this.connection.rollback();
    }
  }

跟commit是一样的处理逻辑。

最后看一下close()方法,如下

  @Override
  public void close() throws SQLException {
    DataSourceUtils.releaseConnection(this.connection, this.dataSource);
  }

这里的关闭,其实也并不是真正的关闭,最终还是交给事务管理器控制,归还连接池而已。

 

好了,介绍完了SpringManagedTransaction,我们再来看看上面多次提到的事务管理器吧!这个其实不在mybatis的范畴了,而是属于spring的内容,就简单的说一下吧。

我们在spring-transaction.xml文件中会配置事务管理器,如下

<!-- 事务对象 -->
	<bean name="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 启用事务 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*"           propagation="REQUIRED"/>
			<tx:method name="modify*"		  propagation="REQUIRED"/>
			<tx:method name="query*"          read-only="true"/>
			<tx:method name="get*"            read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<tx:annotation-driven transaction-manager="transactionManager"/>

	<aop:config>
		<aop:pointcut id="serviceOperation"
			expression="execution(* com.rice.mvc.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
	</aop:config>

这里就会指定事务管理器为DataSourceTransactionManager,它继承于AbstractPlatformTransactionManager implements PlatformTransactionManager,主要的方法有:如下

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

就是获取事务,提交和回滚。关于事务管理器,后面分析spring源码的时候再做介绍。


上一篇  Mybatis源码分析——sql执行过程的类图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值