spring事务管理器原理?mybatis如何集成spring事务管理器?

1. spring事务管理器开启事务做了些什么事情:

首先是如何开启事务: a代码方式:直接调用org.springframework.jdbc.datasource.DataSourceTransactionManager的 b注解方式:@Transaction 这两种方式最终都是调用如下接口的三个方法来实现创建事务,执行commit,执行rollback动作的。

org.springframework.transaction.PlatformTransactionManager#getTransaction

要理解spring事务的原理,重点就是要了解创建事务(getTransaction方法),究竟做了些什么事情。其中@Transaction方式事务,是在AOP代理的拦截方法中执行的创建事务(@Transaction注解method之前创建事务)。

getTransaction方法主要做了以下三件事情:

1.1 创建mysql数据库连接:

因此在进入@Transaction注解method之前就已经创建了数据库连接。

@Transaction进入/出method就创建/关闭数据库连接。

因此@Transaction注解,不能包含太大的方法。包含太大的逻辑,就会导致数据库连接占用时间过长。

1.2 开启mysql事务

执行命令:SET autocommit=0

1.3 将当前事务所使用的连接绑定到ThreadLocal中,供后续执行sql命令使用。

最终存储ThreadLocal为:org.springframework.transaction.support.TransactionSynchronizationManager#resources

// org.springframework.transaction.support.TransactionSynchronizationManager

public abstract class TransactionSynchronizationManager {
   // 事务管理器创建的连接,存储位置
   private static final ThreadLocal<Map<Object, Object>> resources =
         new NamedThreadLocal<>("Transactional resources");
# org.springframework.jdbc.datasource.DataSourceTransactionManager
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   Connection con = null;

   try {
      if (!txObject.hasConnectionHolder() ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
         //1.1 创建mysql数据库连接:
         Connection newCon = obtainDataSource().getConnection();  
         if (logger.isDebugEnabled()) {
            logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
         }
         txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
      }
      
      // 省略部分代码。。。
     	if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
                1.2 开启mysql事务,执行命令:SET autocommit=0
				con.setAutoCommit(false);
			}
			// 省略部分代码。。。
      // Bind the connection holder to the thread.
      if (txObject.isNewConnectionHolder()) {
         1.3 将当前事务所使用的连接绑定到ThreadLocal中,供后续执行sql命令使用。
         TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
      }
   }
   // 省略部分代码。。。
}

2. mybatis如何集成spring事务管理器?

mybatis执行sql会调用 SqlSessionTemplate获取连接,然后会调用 org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator) 方法,这个方法首先会检查事务管理的ThreadLocal中是否已经有了数据库连接,有的话,就直接用此数据库连接(这样就实现了和spring事务管理器的集成,共用数据库连接);如果没有,那么就重新创建一个连接。

因此mybatis不需要做什么配置就可以和spring事务管理器进行自动集成(ThreadLocal是二者集成的纽带),只需要二者使用相同DataSource即可。

# org.mybatis.spring.SqlSessionUtils
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
  
  // 从事务管理器ThreadLocal中获取连接
  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }

  // 没有获取到连接,就重新创建连接
  LOGGER.debug(() -> "Creating a new SqlSession");
  session = sessionFactory.openSession(executorType);

  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

  return session;
}

3. mysql相关命令

查看mysql服务器存在的客户端连接:SELECT * FROM information_schema.PROCESSLIST;

查看mysql所执行的命令(包括查询命令):

show variables where Variable_name="general_log";

set global general_log=on;

show variables where Variable_name="general_log_file";

4. 一个方便调试的数据库Datasource

org.springframework.jdbc.datasource.DriverManagerDataSource

每次获取连接都是通过驱动真实的和mysql服务器创建连接。每次释放连接都是真实的通过驱动和mysql服务解除连接。

5. 参考文档:

经验分享 程序员 微信小程序 职场和发展