Mybatis源碼學習之事務管理(八)
標簽: 事務管理
簡述
在實際開發中,數據庫事務的控制是一件非常重要的工作,本文將學習Mybatis對事務的管理機制。在Mybatis中基于接口 Transaction 將事務分為兩種,一種是JdbcTransaction, 另一種是ManagedTransaction,接下來本文將從源碼的角度解讀 Transaction 接口的不同實現及其區別。
MyBatis將事務抽象成 Transaction 接口,該接口包含了數據庫事務應有的操作,包括創建(create)、提交(commit)、回滾(rollback)、關閉(close),如下類圖
MyBatis的事務管理分為兩種形式:
一、使用JDBC的事務管理機制:利用java.sql.Connection對象完成對事務的提交(commit())、回滾(rollback())、關閉(close())等
二、使用MANAGED的事務管理機制:這種機制是讓容器如(JBOSS,Weblogic)來實現對事務的管理
Mybatis事務的使用
通常我們會通過mybatis.xml配置事務的使用機制,如下 使用JDBC事務管理機制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
Mybatis事務工廠創建
MyBatis事務的創建是交給TransactionFactory 事務工廠來創建的,如果我們將的type 配置為”JDBC”,那么,在MyBatis初始化解析節點時,會根據type=”JDBC”創建一個JdbcTransactionFactory工廠,源碼如下:
/**
* 解析<transactionManager>節點,創建對應的TransactionFactory
*
* @param context xml配置節點信息
* @return TransactionFactory
*/
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
//獲取事務管理機制的類型
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//根據事務管理機制類型創建對應的事務管理工廠
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
如代碼所示,如果type = “JDBC”,則MyBatis會創建一個JdbcTransactionFactory.class 實例;如果type=”MANAGED”,則MyBatis會創建一個MangedTransactionFactory.class實例。
### Mybatis 事務工廠TransactionFactory
事務TransactionFactory定義了創建Transaction的兩個方法:
一個是通過指定的Connection對象創建Transaction,
另外是通過數據源DataSource來創建Transaction。
與JDBC 和 MANAGED兩種Transaction相對應,TransactionFactory有兩個對應的實現的子類:
### Mybatis 事務創建
通過上文的學習,很清楚的知道Transaction對象的創建很容易 通過事務工廠TransactionFactory得到。這里我們以JdbcTransaction為例,看一下JdbcTransactionFactory是怎樣生成JdbcTransaction的,代碼如下:
/**
* JdbcTransaction事務工廠,創建JdbcTransaction實例
*
* @author kaifeng
* @author Clinton Begin
* @see JdbcTransaction
*/
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props) {
}
/**
* 根據給定的數據庫連接conn創建JdbcTransaction實例
*
* @param conn 數據庫連接對象
*/
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
/**
* 根據給定的數據源conn創建JdbcTransaction實例
*
* @param ds 數據源
* @param level 事務隔離級別
* @param autoCommit 事務是否自動提交
*/
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
Mybatis JDBC事務
JdbcTransaction是直接使用JDBC的提交和回滾事務管理機制 。它從dataSource中取得連接connection 來管理transaction 的作用域,connection對象的獲取被延遲到調用getConnection()方法。
JdbcTransaction相當于對java.sql.Connection事務處理進行了包裝(wrapper)。
JdbcTransaction的代碼實現如下:
/**
* JDBC 事務實現
*
* @author kaifeng
* @author Clinton Begin
* @see JdbcTransactionFactory
*/
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;
}
/**
* 事務提交,通過connection.commit()
*/
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
/**
* 事務回滾,通過connection.rollback();
*/
@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;
}
}
Mybatis Managed事務
ManagedTransaction是讓容器來管理事務Transaction的整個生命周期,使用ManagedTransaction的commit和rollback功能不會對事務有任何的影響,什么都不會做,它將事務管理的權利移交給了容器來實現。
如下Managed的實現代碼:
/**
* 讓容器來管理事務的整個生命周期,ManagedTransaction的操作不會對數據庫產生影響,默認情況下它的數據庫連接是關閉的
*
* @author kaifeng
* @author Clinton Begin
* @see ManagedTransactionFactory
*/
public class ManagedTransaction implements Transaction {
private static final Log log = LogFactory.getLog(ManagedTransaction.class);
/**
* 數據源
*/
private DataSource dataSource;
/**
* 事務隔離級別
*/
private TransactionIsolationLevel level;
/**
* 數據庫連接對象
*/
private Connection connection;
/**
* 是否關閉數據庫連接
*/
private final 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;
}
}
在實踐中,MyBatis通常會與Spring集成使用,數據庫的事務是交給Spring進行管理的,在后文我們會介紹Transaction接口的另一個實現—SpringManagedTransaction
智能推薦
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...
猜你喜歡
19.vue中封裝echarts組件
19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...
【一只蒟蒻的刷題歷程】【藍橋杯】歷屆試題 九宮重排 (八數碼問題:BFS+集合set)
資源限制 時間限制:1.0s 內存限制:256.0MB 問題描述 如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。 我們把第一個圖的局面記為:12345678. 把第二個圖的局面記為:123.46758 顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。 本題目的任務是已知九宮的初態...
dataV組件容器寬高發生變化后,組件不會自適應解決方法
項目中需要大屏幕數據展示,于是使用了dataV組件,但是使用是發現拖動瀏覽器邊框,dataV組件顯示異常,如圖: 于是查了官網,官網的解釋如下: 于是按照官網的意思編寫代碼: 于是可以自適應了...