Mybatis事務管理
Mybatis事務管理
事務管理方式
Transaction介面
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
介面實現類有三個分別是JDBCTransaction.class、ManagedTransaction和SpringManagedTransaction(這裡主要說前兩者)
JDBC事務管理機制(JdbcTransaction)
即JDBC使用的到的Connection物件,下面我們看一下Connection的部分原始碼怎麼寫的
PreparedStatement prepareStatement(String sql) throws SQLException; void setAutoCommit(boolean autoCommit) throws SQLException; boolean getAutoCommit() throws SQLException; PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException; void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException; String getClientInfo(String name) throws SQLException; int getHoldability() throws SQLException; void rollback(Savepoint savepoint) throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException;
通過這些commit(),rollback(),close()可以實現事物的提交、回滾、關閉等操作
下面來看看JdbcTransaction的一些方法
protected Connection connection; protected DataSource dataSource; protected TransactionIsolationLevel level; //ManagerTransaction沒有這個屬性 protected boolean autoCommit; //構造方法 public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) { dataSource = ds; level = desiredLevel; autoCommit = desiredAutoCommit; } 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.autoCommit); } 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); } } public void close() throws SQLException { if (connection != null) { resetAutoCommit(); if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + connection + "]"); } connection.close(); } }
MANAGER管理事物(ManagedTransaction)
這種MANAGER機制主要是為了保證mybatis對事務管理的拓展性和靈活性,使用這種機制就可以和其他框架聯合使用,將具體事物的管理都交由第三方框架來進行管理,主要和mybatis整合的框架基本就是Springframework
private DataSource dataSource;
private TransactionIsolationLevel level;
private Connection connection;
//JdbcTransaction類沒有這個屬性
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;
}
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 void close() throws SQLException {
if (this.closeConnection && this.connection != null) {
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + this.connection + "]");
}
this.connection.close();
}
}
public void commit() throws SQLException {
// Does nothing
}
public void rollback() throws SQLException {
// Does nothing
}
與JDBCTransaction不同的是,它沒有嘗試設定自動提交。且在關閉是我們可以看出在JdbcTransaction這個類中關閉方法只是判斷連線是否為空,而ManagerTransaction還必須判斷屬性closeConnection是否是true才進行關閉。而且提交和回滾方法都沒有進行具體的實現,而是提供給spring去實現具體的業務
配置檔案中配置事物執行流程
現在我們來看看在mybatis配置檔案中寫過配置(具體mybatis配置檔案解釋||具體mybatis執行流程)
<?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>
<properties resource="db/mysqlconn.properties"></properties>
<!-- 開啟懶載入 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 將other轉為null值 -->
<setting name="jdbcTypeForNull" value="NULL"/><!-- 不設定這個引數的話有一些資料庫不能識別null,如oracle資料庫 -->
<!-- 指定日誌為log4j -->
<setting name="logImpl" value="LOG4J"/>
<!-- 開啟二級快取 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="pojo" />
</typeAliases>
<environments default="development">
<environment id="development">
<!-- 在這裡我們選擇事務機制,選擇了JDBC -->
<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>
<mappers>
<package name="mapper" />
</mappers>
</configuration>
此時我們設定了事務為JDBC,那麼在之後在呼叫XMLConfigBuilder.environmentsElement()方法時,就會生成一個JDBCTransactionFactory來產生JDBCTransaction物件,程式碼如下:
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//就這一步,將你傳過來的JDBC解析成一個JDBCTransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
下面我們來看事務工廠TransactionFactory介面的原始碼
public interface TransactionFactory {
//設定一些必要的屬性
default void setProperties(Properties props) {
// NOP
}
//和之前的JDBCTransaction和ManagerTransaction的構造方法像吧
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
具體實現類也是和Transaction介面類似,有三個,分別是JDBCTransactionFactory.class、ManagedTransactionFactory和SpringManagedTransactionFactory(主要看前兩者)
JdbcTransactionFactory
@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 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);
}
可以看出JDBCTransactionFactory沒有實現TransactionFactory的setProperties()方法,而ManagerTransactionFactory是實現的
JdbcTemplate
內部為使用者封裝了JDBC的步驟
public JdbcTemplate() {
}
//看得出必須依賴一個數據源
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
setDataSource(dataSource);
setLazyInit(lazyInit);
afterPropertiesSet();
}
//代理模式(執行緒安全)
protected Connection createConnectionProxy(Connection con) {
return (Connection) Proxy.newProxyInstance(
ConnectionProxy.class.getClassLoader(),
new Class<?>[] {ConnectionProxy.class},
new CloseSuppressingInvocationHandler(con));
}
在獲取連線物件Connection的時,使用了JDK動態代理,所以這個類是執行緒安全的,每一次拿的連線物件(Connection)都是代理物件