我们平时使用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文件中会配置事务管理器,如下
这里就会指定事务管理器为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执行过程的类图