本文將會介紹Mybatis的事務管理機制的原理,首先介紹下事務管理的特質、在Mybatis中是如何創建事務、事務有哪幾種類型、不同事務的源碼實現、并比較其中的不同、并總結事務的原理。最后分享學習事務常見的小知識。
事務的概述
對于數據庫事務,具有如下幾種特質:
- 創建(
create
) - 提交(
commit
) - 回滾(
rollback
) -
關閉(
close
)
對應地,MyBatis將事務抽象成了Transaction
接口,源碼如下:
可以看出,這個接口中包含最基本 的
getConnection、commit、rollback、close
方法,任何實現對事物管理都需要實現這幾個方法。
在
Mybatis
中實現事務的管理分為如下兩種:
-
JDBC
的事務管理機制:即利用java.sql.Connection
對象完成對事務的提交(commit()
)、回滾(rollback()
)、關閉(close()
)等 -
MANAGED
的事務管理機制:這種機制MyBatis
自身不會去實現事務管理,而是讓程序的容器如(JBOSS
,Weblogic
)來實現對事務的管理
事務的創建
Mybatis
在初始化的時候,會加載解析Mybatis
的xml配置文件,在xml文件中若配置了事務管理的類型,<transactionManager>
的type
配置為"JDBC"
,那么,在MyBatis
初始化解析<environment>
節點時,會根據
-
type="JDBC"
創建一個JdbcTransactionFactory
工廠,JdbcTransactionFactory
能夠創建JDBC
類型的事務管理機制 -
type="MANAGED"
創建一個MangedTransactionFactory
工廠,MangedTransactionFactory
能夠創建MANAGED
類型的事務管理機制
源碼截圖如下:
下面我們來看看他們的實現細節:
JdbcTransaction
的創建
JdbcTransactionFactory
類會根據 DataSource
、隔離級別、是否自動提交 這三個參數創建Transacion
,也可以根據給定的數據庫連接Connection
創建Transaction
JdbcTransactionFactory
的源碼如下:
public class JdbcTransactionFactory implements TransactionFactory {
public void setProperties(Properties props) {
}
/**
* 根據給定的數據庫連接Connection創建Transaction
* @param conn Existing database connection
* @return
*/
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
/**
* 根據DataSource、隔離級別和是否自動提交創建Transacion
*
* @param ds
* @param level Desired isolation level
* @param autoCommit Desired autocommit
* @return
*/
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
JdbcTransaction
的實現
JdbcTransaction
直接使用JDBC
的提交和回滾事務管理機制 。它依賴與從dataSource
中取得的連接connection
來管理transaction
的作用域,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;
//根據 DataSource、隔離級別、是否自動提交 三個參數創建Transacion
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
this.dataSource = ds;
this.level = desiredLevel;
this.autoCommmit = desiredAutoCommit;
}
//根據給定的數據庫連接Connection創建Transaction
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
public Connection getConnection() throws SQLException {
if(this.connection == null) {
this.openConnection();
}
return this.connection;
}
//使用 java.sql.Connection 的 commit
public void commit() throws SQLException {
if(this.connection != null && !this.connection.getAutoCommit()) {
if(log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + this.connection + "]");
}
this.connection.commit();
}
}
//使用 java.sql.Connection 的 rollback
public void rollback() throws SQLException {
if(this.connection != null && !this.connection.getAutoCommit()) {
if(log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + this.connection + "]");
}
this.connection.rollback();
}
}
//使用 java.sql.Connection 的 close
public void close() throws SQLException {
if(this.connection != null) {
this.resetAutoCommit();
if(log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if(this.connection.getAutoCommit() != desiredAutoCommit) {
if(log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + this.connection + "]");
}
this.connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException var3) {
throw new TransactionException("Error configuring AutoCommit. Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: " + desiredAutoCommit + ". Cause: " + var3, var3);
}
}
protected void resetAutoCommit() {
try {
if(!this.connection.getAutoCommit()) {
if(log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + this.connection + "]");
}
this.connection.setAutoCommit(true);
}
} catch (SQLException var2) {
if(log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true before closing the connection. Cause: " + var2);
}
}
}
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());
}
this.setDesiredAutoCommit(this.autoCommmit);
}
public Integer getTimeout() throws SQLException {
return null;
}
}
總結:
- 當使用DataSource創建數據庫連接時,數據庫的事務隔離級別使用DataSource默認的事務隔離級別
- 當使用 DataSource、隔離級別、是否自動提交 三個參數創建
JdbcTransaction
時,會使用傳入的參數來設定隔離級別和是否自動提交- 對select不進行事務控制
JdbcTransaction
的事務,原理上就是封裝了一層JDBC
的方法
ManagedTransaction的實現
ManagedTransaction
讓容器來管理事務Transaction
的整個生命周期,使用ManagedTransaction
的commit
和rollback
功能不會對事務有任何的影響,它什么都不會做,它將事務管理的權利移交給了容器來實現
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;
}
public Connection getConnection() throws SQLException {
if(this.connection == null) {
this.openConnection();
}
return this.connection;
}
//不做任何處理
public void commit() throws SQLException {
}
//不做任何處理
public void rollback() throws SQLException {
}
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());
}
}
public Integer getTimeout() throws SQLException {
return null;
}
}
關于事務的隔離級別
先對不同隔離級別涉及到的名詞解釋:
? 臟讀: 對于兩個事物 T1、T2,T1 讀取了已經被 T2 更新但還沒有被提交的字段。之后, 若 T2 回滾,T1讀取的內容就是臨時且無效的。
? 不可重復讀: 對于兩個事物 T1、T2, T1 讀取了一個字段, 然后 T2 更新了該字段。 之后, T1再次讀取同一個字段, 值就不同了。
? 幻讀: 對于兩個事物 T1、T2, T1 從一個表中讀取了一個字段, 然后 T2 在該表中插入了一些新的行。 如果 T1 再次讀取同一個表, 就會多出數據
具體的隔離級別定義:
- READ UNCOMMITTED(讀未提交數據) :允許事務讀取未被其他事務提交的變更,臟讀、不可重復讀和幻讀的問題都會出現
- READ COMMITED(讀已提交數據) :只允許事務讀取已經被其他事務提交的變更,可以避免臟讀,但不可重復讀和幻讀問題仍然會出現
- REPEATABLE READ(可重復讀) :確保事務可以多次從一個字段中讀取相同的值,在這個事務持續期間,禁止其他事務對這個字段進行更新,可以避免臟讀和不可重復讀,但幻讀的問題依然存在
- SERIALIZABLE(串行化) :確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新和刪除操作,所有并發問題都可以避免,但性能十分低
Oracle
默認的事務隔離級別為: READ COMMITED(讀已提交數據)
Mysql
默認的事務隔離級別為: REPEATABLE READ(可重復讀)
以上就是《Mybatis原理--事務管理》的全部內容,如有不正確的地方,請讀者指正,互相學習,共同進步,謝謝。