一、Mybatis事務
1、事務管理方式
Mybatis中的事務管理方式有兩種:
1、JDBC的事務管理機制,即使用JDBC事務管理機制進行事務管理
2、MANAGED的事務管理機制,Mybatis沒有實現對事務的管理,而是通過容器來實現對事務的管理
其中,Mybatis提供了事務的接口: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
*/
//獲取事務timeout
Integer getTimeout() throws SQLException;
}
Transaction有兩個實現類:JdbcTransaction和ManagedTransaction,分別對應兩種事務管理方式。
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()) {
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;
}
}
JdbcTransaction通過使用jdbc提供的方式來管理事務,通過Connection提供的事務管理方法來進行事務管理,只是將JDBC的事務管理進行了封裝。
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 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進行事務管理,這會在后面的Spring源碼中說到。
可參考:https://blog.csdn.net/qq924862077/article/details/525997851.2
2、事務配置方式
Mybatis的事務管理方式的配置是在核心配置文件中進行的,它是在configuration標簽下的environments中與數據源一起配置的,可以配置多個,如下所示:
<environments default="development">
<environment id="development">
//配置事務管理方式
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
二、事務隔離級別
在上面的源碼中,我們看到TransactionIsolationLevel—事務隔離級別,Mybaais中定義了五種隔離級別,代碼如下所示:
public enum TransactionIsolationLevel {
NONE(Connection.TRANSACTION_NONE),
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
private final int level;
private TransactionIsolationLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}
其中四種是一般數據庫的事務隔離級別,從高到底以此為:Read uncommitted(讀未提交)、Read committed(讀已提交)、Repeatable read(可重復讀)、Serializable(可串行化),這幾個級別主要用於解決臟讀、不可重復讀、幻讀等問題,其總結如下表:
級別 | 臟讀 | 不可重復讀 | 幻讀 | 說明 |
---|---|---|---|---|
讀未提交 | Y | Y | Y | 一個更新語句沒有提交,但是別的事務可以讀到這個改變.這是很不安全的。允許任務讀取數據庫中未提交的數據更改,也稱為臟讀。 |
讀已提交 | X | Y | Y | 語句提交以后即執行了COMMIT以后別的事務就能讀到這個改變. 只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別,可防止臟讀。 |
可重復讀 | X | X | Y | 同一個事務里面先后執行同一個查詢語句的時候,得到的結果是一樣的.在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標准中,該隔離級別消除了不可重復讀,但是還存在幻象讀。 |
可串行化 | X | X | X | 事務執行的時候不允許別的事務並發執行. 完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞。 |
注:Y表示出現,X表示不出現
參考自:https://blog.csdn.net/qq924862077/article/details/52599961
Mybatis添加的隔離級別NONE,可在DefaultSqlSessionFactory中創建SqlSession時,設置數據庫的事務隔離級別,以及通過設置autoCommit來設置事務的提交方式。代碼參見DefaultSqlSessionFactory,示例代碼如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
三、事務工廠
此外,在Mybatis中還提供了事務工廠:TransactionFactory,代碼如下所示:
public interface TransactionFactory {
//配置工廠屬性
void setProperties(Properties props);
//通過Connection獲取事務
Transaction newTransaction(Connection conn);
//通過數據庫,事務等級,是否自動提交創建事務
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
類似的,TransactionFactory也有兩個實現類:JdbcTransactionFactory和ManagedTransactionFactory
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) {
return new ManagedTransaction(ds, level, closeConnection);
}
}