Mybatis中的事務管理器詳述
在上篇文章<>中,我們結合原始碼對mybatis中的資料來源和連線池進行了比較詳細的說明。在這篇文章中,我們講講相關的另外一個主題——事務管理器。
在前面的文章中,我們知道mybatis支援兩種事務型別,分別為JdbcTransaction和ManagedTransaction。接下來,我們從mybatis的xml配置檔案入手,講解事務管理器工廠的建立,然後講述事務的建立和使用,最後分析這兩種事務的實現和兩者的區別。
我們先看看配置檔案中相關的配置:
Mybatis定義了一個事務型別介面Transaction,JdbcTransaction和ManagedTransaction兩種事務型別都實現了Transaction介面。我們看看Transaction這個介面的定義:
public interface Transaction {
/**
* 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;
}
在事務介面中,定義了若干方法,如下結構所示:
事務的繼承關係如下:
JdbcTransaction和ManagedTransaction的區別如下:
JdbcTransaction:利用java.sql.Connection物件完成對事務的提交(commit())、回滾(rollback())、關閉(close())等;
ManagedTransaction:MyBatis自身不會去實現事務管理,而是讓程式的容器來實現對事務的管理;
那麼在mybatis中又是怎麼使用事務管理器的呢?首先需要根據xml中的配置確定需要建立什麼樣的事務管理器,然後從事務管理器中獲取相應的事務。
在mybatis初始化的時候,在解析<transactionManager>節點的時候,根據設定的type型別去初始化相應的事務管理器,解析原始碼如下所示:
/**
* 解析<transactionManager>節點,建立對應的TransactionFactory
* @param context
* @return
* @throws Exception
*/
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
/*
在Configuration初始化的時候,會通過以下語句,給JDBC和MANAGED對應的工廠類
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
下述的resolveClass(type).newInstance()會建立對應的工廠例項
*/
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
從程式碼可以看出來,如果type配置成JDBC,則建立一個JdbcTransactionFactory例項,如果type配置成MANAGED,則會建立一個ManagedTransactionFactory例項。這兩個事務管理器型別都實現了mybatis定義的TransactionFactory介面。
事務管理器工廠介面的定義如下所示:
public interface TransactionFactory {
/**
* Sets transaction factory custom properties.
* @param props
*/
void setProperties(Properties props);
/**
* Creates a {@link Transaction} out of an existing connection.
* @param conn Existing database connection
* @return Transaction
* @since 3.1.0
*/
Transaction newTransaction(Connection conn);
/**
* Creates a {@link Transaction} out of a datasource.
* @param dataSource DataSource to take the connection from
* @param level Desired isolation level
* @param autoCommit Desired autocommit
* @return Transaction
* @since 3.1.0
*/
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
從介面定義看,不管是JdbcTransactionFactory,還是ManagedTransactionFactory,都需要自行實現事務Transaction的建立工作。我們從原始碼上看看這兩個類都是怎麼定義實現的。
JdbcTransactionFactory定義如下:
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) {
}
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
ManagedTransactionFactory定義如下:
public class ManagedTransactionFactory implements TransactionFactory {
private boolean closeConnection = true;
@Override
public void setProperties(Properties props) {
if (props != null) {
String closeConnectionProperty = props.getProperty("closeConnection");
if (closeConnectionProperty != null) {
closeConnection = Boolean.valueOf(closeConnectionProperty);
}
}
}
@Override
public Transaction newTransaction(Connection conn) {
return new ManagedTransaction(conn, closeConnection);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// Silently ignores autocommit and isolation level, as managed transactions are entirely
// controlled by an external manager. It's silently ignored so that
// code remains portable between managed and unmanaged configurations.
return new ManagedTransaction(ds, level, closeConnection);
}
}
從原始碼看,JdbcTransactionFactory會建立JDBC型別的事務JdbcTransaction,而ManagedTransactionFactory則會建立ManagedTransaction。
接下來,我們再來看看這兩種型別的事務型別。
先從JdbcTransaction看起吧,這種型別的事務就是使用Java自帶的Connection來實現事務的管理。connection物件的獲取被延遲到呼叫getConnection()方法。如果autocommit設定為on,開啟狀態的話,它會忽略commit和rollback。直觀地講,就是JdbcTransaction是使用的java.sql.Connection 上的commit和rollback功能,JdbcTransaction只是相當於對java.sql.Connection事務處理進行了一次包裝(wrapper),Transaction的事務管理都是通過java.sql.Connection實現的。
JdbcTransaction的程式碼實現如下:
public class JdbcTransaction implements Transaction {
private static final Log log = LogFactory.getLog(JdbcTransaction.class);
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
protected boolean autoCommmit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommmit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
if (log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
// Only a very poorly implemented driver would fail here,
// and there's not much we can do about that.
throw new TransactionException("Error configuring AutoCommit. "
+ "Your driver may not support getAutoCommit() or setAutoCommit(). "
+ "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e);
}
}
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
// MyBatis does not call commit/rollback on a connection if just selects were performed.
// Some databases start transactions with select statements
// and they mandate a commit/rollback before closing the connection.
// A workaround is setting the autocommit to true before closing the connection.
// Sybase throws an exception here.
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(true);
}
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true "
+ "before closing the connection. Cause: " + e);
}
}
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommmit);
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}
最後我們再來看看ManagedTransaction物件,這個物件因為是將事務的管理交給容器去控制,所以,這裡的ManagedTransaction是沒有做任何控制的。我們先來看看原始碼:
public class ManagedTransaction implements Transaction {
private static final Log log = LogFactory.getLog(ManagedTransaction.class);
private DataSource dataSource;
private TransactionIsolationLevel level;
private Connection connection;
private boolean closeConnection;
public ManagedTransaction(Connection connection, boolean closeConnection) {
this.connection = connection;
this.closeConnection = closeConnection;
}
public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
this.dataSource = ds;
this.level = level;
this.closeConnection = closeConnection;
}
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}
@Override
public void commit() throws SQLException {
// Does nothing
}
@Override
public void rollback() throws SQLException {
// Does nothing
}
@Override
public void close() throws SQLException {
if (this.closeConnection && this.connection != null) {
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}
從原始碼我們可以看到,ManagedTransaction的commit和rollback方法是沒有做任何事情的,它將事務交由了更上層的容易來進行控制和實現。至此,關於事務管理器我們描述的已經差不多了,如果需要深究可以自己再去研究研究。
如果想了解我最新的博文,請關注我的部落格,謝謝。如果想看到更多相關技術文章並願意支援我繼續寫下去,歡迎給我打賞,對您的打賞我表示感謝。