1. 程式人生 > 實用技巧 >Mybatis事務管理

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)都是代理物件