要想使用spring的事务,要加入mybatis-spring依赖包
<!-- 引用插件依赖:MyBatis整合Spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<context:property-placeholder location="classpath:properties/*.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
</bean>
<!-- sqlsessionfactory -->
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
</bean>
<!-- mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.taotao.mapper" />
</bean>
配置sqlSessionFactory给spring来管理
SqlSessionFactoryBean这是一个FactoryBean相信读过spring源码的都知道
org.mybatis.spring.SqlSessionFactoryBean#getObject
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
//sqlSessionFactory没有构建好,那么就调用afterPropertiesSet
afterPropertiesSet();
}
//返回构建好的sqlSessionFactory
return this.sqlSessionFactory;
}
org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
.....
//构建SqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
.....
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
......
return this.sqlSessionFactoryBuilder.build(configuration);
}
这段代码可以看出transactionFactory 等于null的时候回去创建一个SpringMngTransFactory
并且把transFactory设置给Environment然后再把Environment设置给configuration,最后
sqlSessionFactoryBuilder.build(configuration);返回sqlSessionFactory
在DefaultSqlSessionFactory#openSessionFromConnection方法中创建了Transaction
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
//从configuration中取出environment对象
final Environment environment = configuration.getEnvironment();
//从environment中取出TransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建Transaction
final Transaction tx = transactionFactory.newTransaction(connection);
//创建包含事务操作的执行器
final Executor executor = configuration.newExecutor(tx, execType);
//构建包含执行器的SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
DefaultSqlSessionFactory#getTransactionFactoryFromEnvironment
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
//如果没有配置事务工厂,则返回托管事务工厂
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
这里返回SpringManagedTransactionFactory对象,然后调用
SpringManagedTransactionFactory#newTransaction(javax.sql.DataSource, org.apache.ibatis.session.TransactionIsolationLevel, boolean)
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
}
也就是说mybatis的执行事务的事务管理器就切换成了SpringManagedTransaction
我们再看org.mybatis.spring.transaction.SpringManagedTransaction#getConnection
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
//获取connection
openConnection();
}
return this.connection;
}
private void openConnection() throws SQLException {
//调用DataSourceUtils 拿到数据库连接
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
.....
org.springframework.jdbc.datasource.DataSourceUtils#getConnection
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}
此处进入spring-jdbc的模块代码,猜想:
- spring的@trans事务操作的conn会保存在一个ThreadLocal中
- 当mybatis操作数据库时从这个ThreadLocal中去取conn,这样就可以做到spring来控制mybatis的数据库操作了
下面我们就来验证我们的猜想:
org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
//获取ConnectionHolder
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
/**
* conHolder不为空 有链接
*/
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
//如果conHolder没有连接
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
//取出一个连接设置给conHolder
conHolder.setConnection(fetchConnection(dataSource));
}
//返回conHolder中的连接
return conHolder.getConnection();
}
.....
想看怎么获取connHolder
org.springframework.transaction.support.TransactionSynchronizationManager#getResource
@Nullable
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
//获取ConnectionHolder
Object value = doGetResource(actualKey);
....
return value;
}
//保存数据库连接的ThreadLocal
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
@Nullable
private static Object doGetResource(Object actualKey) {
/**
* 从threadlocal <Map<Object, Object>>中取出来当前线程绑定的map
* map里面存的是<dataSource,ConnectionHolder>
*/
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
//map中取出来对应dataSource的ConnectionHolder
Object value = map.get(actualKey);
// Transparently remove ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
可以看到确实是从ThreadLocal中取出来的conn,而spring自己的事务也是操作的这个ThreadLocal中的conn来进行事务的开启和回滚
然后就取出来connHolder中的conn返回,当取出来的conn为空时候,调用
org.springframework.jdbc.datasource.DataSourceUtils#fetchConnection
private static Connection fetchConnection(DataSource dataSource) throws SQLException {
//从数据源取出来conn
Connection con = dataSource.getConnection();
if (con == null) {
throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
}
return con;
}
然后把从数据源取出来的连接返回
到此mybatis的事务是怎么被spring管理的就显而易见了