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服务解除连接。