我們處於的位置
我們要清楚現在的情況.
現在我們已經呼叫了SqlSessionFactoryBuilder
的build方法生成了SqlSessionFactory
物件.
但是如標題所說,要想生成sqlsession
還要另一步SqlSessionFactory
呼叫openSession()
方法生成sqlsession
;
這就要從上一部分程式碼講起
上文講到
我們建立的實際上是一個叫做DefaultSqlSessionFactory
的類,實際上他是一個SqlSessionFactory
介面(沒錯,這玩應是介面)的實現類.
既然sqlsession是由opensession產生的,那我們就先看這個方法.
說一嘴題外話就是自動提交也是在這個部分設定的,下面是如果你設定了autocommit的情況.
public SqlSession openSession(boolean autoCommit) {
//this.configuration.getDefaultExecutorType()值為 ExecutorType.SIMPLE;
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, autoCommit);
}
引數中 configuration 獲取了預設的執行器 “SIMPLE”.
DefaultSqlSessionFactory
類
呼叫了一個同一個類中openSessionFromDataSource
方法.
在這個類中是如下執行流程
所要知道的一部分知識.
MyBatis 核心配置綜述之 Configuration詳解
其實就是資料庫連線那個部分.
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//從configuration物件中得到環境配置的物件
final Environment environment = configuration.getEnvironment();
//這個物件被用來建立一個事務工廠->一號分支
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//事務工廠建立一個事務物件->二號分支
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//而 configurationye 則會根據事務物件和執行器型別建立一個執行器。
->三號分支
final Executor executor = configuration.newExecutor(tx, execType);
//返回一個預設的DefaultSqlSession物件
->四號分支
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
現在我們要從一號分支開始
一號分支
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
這個程式碼如下:
我們發現有兩種可能性.
如果傳進來的值沒有設定ManagedTransactionFactory()
而反之則會執行environment.getTransactionFactory()
這兩者產生的物件都實現了 TransactionFactory
介面.
這裡ManagedTransactionFactory()
是沒有標籤時生成的物件.其核心就是一句
private boolean closeConnection = true;
的屬性.
我們不必過於關注這個部分.
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
//如果沒有目標標籤
return new ManagedTransactionFactory();
}
//如果有目標標籤
return environment.getTransactionFactory();
}
environment.getTransactionFactory()
產生的東西才是重點.
呼叫環境物件的getTransactionFactory
方法,該方法和我們配置的一樣返回了一個 JdbcTransactionFactory
,而實際上,TransactionFactory
只有2個實現類,一個是 ManagedTransactionFactory
(沒有標籤時返回的),一個是 JdbcTransactionFactory
(有標籤時返回的)。
至此一號分支結束,從此看來,一號分支實際上是將environment物件包裝成一個工廠物件.
請返回一號分支之前部分繼續.
分支二
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
我們回到openSessionFromDataSource
方法,獲取了 JdbcTransactionFactory
後,呼叫 JdbcTransactionFactory
的newTransaction
方法建立一個事務物件.
當然因為程式碼中採用TransactionFactory
介面作為宣告物件.所以無論分之一傳回來的是哪個工廠物件.在分支二中都可以執行.
我們先講 JdbcTransactionFactory
的情況.
分支二中呼叫的是這個newTransaction
方法.(還有一個過載的)
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
這就到了另一個類中JdbcTransaction
中.
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 {
}
public void commit() throws SQLException {
}
public void rollback() throws SQLException {
}
public void close() throws SQLException {
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
}
protected void resetAutoCommit() {
}
protected void openConnection() throws SQLException {
}
}
其實只要看了程式碼你就會發現,這個類中的方法,和我們呼叫session的方法高度重合.比如commit,rollback等等.而且還能設定事務的隔離級別
所以我們有理由認為,這個類就是對jdbc連線部分的封裝.
總結
至此分支二結束,我們對於JdbcTransaction
物件.另一個則是ManagedTransaction
物件(這個沒講....)
分支三
第三分支我們將回到Configuration
物件.
Configuration物件
法此時已經建立好事務物件。接下來將事務物件執行器作為引數執行 configuration 的 newExecutor 方法來獲取一個 執行器類。我們看看該方法實現:
首先第一句將判斷是否傳入了一個excutorType引數,如果沒有就用預設的引數.
也就是 ExecutorType.SIMPLE(前面出現過),然後根據執行的型別來建立不同的執行器,預設是 SimpleExecutor 執行器.
Mybatis有三種基本的Executor執行器:
-
SimpleExecutor:每執行一次update或select,就開啟一個Statement物件,用完立刻關閉Statement物件。
-
ReuseExecutor:執行update或select,以sql作為key查詢Statement物件,存在就使用,不存在就建立,用完後,不關閉Statement物件,而是放置於Map<String, Statement>內,供下一次使用。簡言之,就是重複使用Statement物件。
-
BatchExecutor:執行update(沒有select,JDBC批處理不支援select),將所有sql都新增到批處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個Statement物件,每個Statement物件都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。
作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命週期範圍內。
然後我們看下一句部分
Executor executor;
//看看上文.這是根據傳入的內容不同,最終結果是
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
我們先將 BatchExecutor執行器.
該類包裝了事務物件,延遲載入的佇列,本地快取,永久快取,配置物件,還包裝了自己。
傳入的兩個引數分別為儲存了配置資訊的Configuration物件,以及封裝了jdbc中連線資料庫部分程式碼的
JdbcTransaction
物件.
回到 newExecutor 方法,判斷是否使用快取,預設是true, 則將剛剛的執行器包裝到新的 CachingExecutor 快取執行器中。最後將執行器新增到所有的攔截器中(如果配置了話),我們這裡沒有配置。
到此分支三結束
總結:
我們從用從分支二得到的物件,構建了一個執行器.這個執行物件,包括事務物件(即連jdbc連線部分的控制封裝.JdbcTransaction
),延遲載入的佇列,本地快取,永久快取,配置物件(Configuration),還包裝了自己。
四號分支
我們已經有了執行器,此時建立 DefaultSqlSession 物件,攜帶 configuration, executor, autoCommit 三個引數,該構造器就是簡單的賦值過程。我們有必要看看該類的結構:
該類包含了常用的所有方法,包括事務方法,可以說,該類封裝了執行器和事務類。而執行器才是具體的執行工作人員。
至此,我們已經完成了 SqlSession 的建立過程。