MyBatis的事務管理和快取機制
一、MyBatis的事務管理
1.事務的概念:事務是一個或幾個操作組成的一個整體執行單元,它們要麼全部執行,要麼全不執行,不能只執行其中的某幾個操作;可以理解為一個事務是一個程式中執行的最小單元。
2.事務的特性:事務包含四個特性:原子性、一致性、隔離性、永續性,簡稱ACID性,具體詳解如下
·原子性:事務是應用中最小的執行單位,就像自然界中原子是最小的顆粒一樣,具有不可分隔的特性,事務是應用程式中不可分隔的最小邏輯執行單元。
·一致性:事務執行的結果,必須使資料庫從一種一致狀態,變成另為一種一致狀態。例如事務中包含兩條需要執行的sql語句,要麼兩條語句都執行成功,要麼都不成功,這就是一致性。
·隔離性:各個事務的執行互不干擾,任意一個事務的內部操作對其他併發的事務,都是隔離的。即,併發執行的事務之間是不會互相干擾的。
·持續性:指事務一旦提交,對資料的修改就是永久的,不可逆的。
3.事務的執行動作:資料庫的事務執行動作可分為:建立(create)、提交(commit)、回滾(rollback)、關閉(close),其對應的動作都抽象到了Transaction介面中,其原始碼如下:
public interface Transaction { //獲取資料庫連線 java.sql.Connection getConnection() throws java.sql.SQLException; //提交 void commit() throws java.sql.SQLException; //回滾 void rollback() throws java.sql.SQLException; //關閉資料庫連線 void close() throws java.sql.SQLException; }
4.MyBatis的事務管理分類:
·使用JDBC的事務管理機制 —— 利用java.sql.Connnection物件完成對事務的提交、回滾和關閉等操作。
·使用MANAGED的事務管理機制 —— 對於這種機制,MyBatis自身不會去實現事務管理,而是讓容器如JBOSS等來實現對事務的管理。
5.事務的使用流程。
5.1事務的配置,首先在MyBatis的根配置檔案mybatis-config.xml中定義如下資訊:
<environment id="mysql">
<!--指定事務管理型別,type="JDBC"指直接使用JDBC的提交和回滾設定,type=“MANAGED”指讓容器實現對事務的管理-->
<transactionManager type="JDBC" />
<!-- <transactionManager type="MANAGED">
<property name="closeConnection" value="false" />
</transactionManager>-->
<!-- dataSource資料來源配置,POOLED是JDBC連線物件的資料來源連線池的實現。-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root"/>
<property name="password" value="123456" />
</dataSource>
</environment>
5.2事務工廠的建立:MyBatis的事務建立是由org.apache.ibatis.transaction.TransactionFactory事務工廠來完成的,會根據<transactionManager>的type型別來建立是JdbcTransactionFactory工廠還是ManagedTransactionFactory工廠,其原始碼如下:
public interface TransactionFactory {
void setProperties(java.util.Properties properties);
org.apache.ibatis.transaction.Transaction newTransaction(java.sql.Connection connection);
org.apache.ibatis.transaction.Transaction newTransaction(javax.sql.DataSource dataSource, org.apache.ibatis.session.TransactionIsolationLevel transactionIsolationLevel, boolean b);
}
5.3 事務工廠TransactionFactory:通過TransactionFactory可以獲得到Transaction物件的例項,以JdbcTransaction為例,其原始碼如下:
public class JdbcTransactionFactory implements TransactionFactory {
public void setProperties(Properties props) {
}
//根據給定的資料庫連線Connection建立Transaction
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
//根據DataSource、隔離級別和是否自動提交建立Transaction
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
5.4 JdbcTransaction :JdbcTransaction可直接使用JDBC提交和回滾事務管理機制,JdbcTransaction是使用了java.sql.Connection上的commit和rollback功能來完成事務操作,其實JdbcTransaction只是把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;
}
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
//使用connection的commit;
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
//使用connection的rollback;
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
//使用connection的close
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
}
5.5 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) {
openConnection();
}
return this.connection;
}
public void commit() throws SQLException {
// Does nothing
}
public void rollback() throws SQLException {
// Does nothing
}
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("Openning JDBC Connection");
}
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
}
二、MyBatis的快取機制
1、一級快取(SqlSession級別) ——
1.1 含義:在操作資料庫時需要建立SqlSession物件,在物件中有一個HashMap用來儲存快取資料,並且不同的SqlSession之間的快取資料區域是不會互相影響的。
1.2 作用域:一級快取的作用域是SqlSession範圍,就是當同一個SqlSession中執行兩次相同的sql查詢語句時,第一次回去資料庫中查詢資料並寫到快取中,第二次在查詢的時候,不會再去資料庫中去查詢,而是直接在快取中讀取資料。在使用時需要注意:當SqlSession執行DML操作(insert、update、delete)時,MyBatis會清空SqlSession中的一級快取,這樣做的目的是保證快取中的資料永遠是最新的資料,防止出現髒讀資料。
2.二級快取(mapper級別) ——
1.1 含義:使用二級快取時,多個SqlSession共享一個Mapper的sql語句去操作資料庫,得到的資料,同樣用HashMap來儲存快取資料,相比較於一級快取,二級快取的範圍更大,多個SqlSession共享二級快取,二級快取時跨SqlSession的。
1.2作用域:二級快取的作用域是mapper的同一個namespace,當不同的SqlSession執行相同的namespace下的sql語句,並向sql語句中傳遞的引數也相同時,第一次回去資料庫中查詢資料並寫到快取中,第二次在查詢的時候,不會再去資料庫中去查詢,而是直接在快取中讀取資料。
1.3 二級快取的使用配置:因為MyBatis預設沒有開啟二級快取,需要在setting全域性引數中開啟二級快取,其mybatis-config.xml配置檔案如下:
<settings>
<!--開啟二級快取-->
<setting name="cacheEnabled" value="true"/>
</settings>
在需要開啟二級快取的mapper檔案中加入如下配置:
<cache eviction="LRU" flushInterval="10000" size="512" readOnly="true" />
其上述元素配置詳解如下:
·eviction —— 收回策略,預設為LRU,一共有如下幾種回收策略
·LRU —— 最近最少使用的策略,移除最長時間不被使用的物件。
·FIFO —— 先進先出策略,按物件進入快取的順序來移除它們。
·SOFT —— 軟引用策略,移除基於垃圾回收器狀態和軟引用規則的物件。
·WEAK —— 弱引用策略,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。
·flushInterval —— 重新整理間隔,可以被設定為任意的正整數,它的單位毫秒,預設情況下是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。
·size —— 快取數目,可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的可用記憶體資源數目。預設值是1024。
·readOnly —— 只讀屬性,可以被設定為 true或false,只讀的快取會給所有呼叫著返回快取物件的相同例項,因此這些物件不能被修改,可讀寫的快取會返回快取物件的拷貝(通過序列化)。這樣做會慢一些,但是安全,因此預設是false。