詳解MyBatis的SqlSession獲取流程
前言
已知在MyBatis的使用中,使用MyBatis時會先讀取配置文件mybatis-config.xml為字符流或者字節(jié)流,然后通過SqlSessionFactoryBuilder基于配置文件的字符流或字節(jié)流來構建SqlSessionFactory,然后再通過SqlSessionFactory的openSession() 方法獲取SqlSession,示例代碼如下所示。
public static void main(String[] args) throws Exception { String resource = "mybatis-config.xml"; SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(Resources.getResourceAsStream(resource)); SqlSession sqlSession = sqlSessionFactory.openSession(); }
上述示例代碼中的SqlSessionFactory實際為DefaultSqlSessionFactory,本篇文章將對DefaultSqlSessionFactory獲取SqlSession的流程進行學習。
正文
DefaultSqlSessionFactory的openSession() 方法如下所示。
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); }
繼續(xù)看openSessionFromDataSource() 方法的實現(xiàn),如下所示。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // Environment中包含有事務工廠和數(shù)據(jù)源 final Environment environment = configuration.getEnvironment(); // 獲取事務工廠 // 如果配置了JDBC事務,則獲取JDBC事務工廠,否則獲取MANAGED事務工廠 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 創(chuàng)建事務 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 創(chuàng)建執(zhí)行器 final Executor executor = configuration.newExecutor(tx, execType); // 構建DefaultSqlSession并返回 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
openSessionFromDataSource() 方法會先從Configuration中將Environment獲取出來,Environment中包含有事務工廠和數(shù)據(jù)源,Environment是在MyBatis配置文件中進行配置并會在加載MyBatis配置文件時被添加到Configuration中。
MyBatis的配置文件中可以配置兩種事務工廠,為JDBC事務工廠和MANAGED事務工廠,它們分別可以創(chuàng)建JDBC事務和MANAGED事務,類圖如下所示。
獲取事務工廠的getTransactionFactoryFromEnvironment() 方法如下所示。
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { if (environment == null || environment.getTransactionFactory() == null) { // 配置了事務工廠則使用配置的事務工廠 // MyBatis可以配置JDBC和MANAGED這兩種事務工廠 return new ManagedTransactionFactory(); } // 沒做配置的情況下使用MANAGED事務工廠 return environment.getTransactionFactory(); }
MyBatis中如果使用JDBC事務,則事務的管理由java.sql.Connection完成,如果使用MANAGED事務,則MyBatis會將事務的管理交由WEB容器(Tomcat,JBoss等)來完成。以commit() 和rollback() 方法為例,看下JdbcTransaction和ManagedTransaction對這兩個方法的實現(xiàn),如下所示。
public class JdbcTransaction implements Transaction { // ...... @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(); } } // ...... } public class ManagedTransaction implements Transaction { // ...... @Override public void commit() throws SQLException { // 不做任何事情 } @Override public void rollback() throws SQLException { // 不做任何事情 } // ...... }
回到openSessionFromDataSource() 方法,創(chuàng)建好事務后,會構建執(zhí)行器Executor,看一下newExecutor(Transaction transaction, ExecutorType executorType) 方法的實現(xiàn),如下所示。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; // 根據(jù)ExecutorType的枚舉值創(chuàng)建對應類型的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); } // 如果MyBatis配置文件中開啟了二級緩存 if (cacheEnabled) { // 創(chuàng)建CachingExecutor作為Executor的裝飾器,為Executor增加二級緩存功能 executor = new CachingExecutor(executor); } // 將插件邏輯添加到Executor中 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
在上面構建執(zhí)行器Executor的方法中,主要是做了三件事情,解釋如下。
第一件事情
,根據(jù)ExecutorType的枚舉值創(chuàng)建對應類型的Executor,ExecutorType枚舉值如下所示。
public enum ExecutorType { SIMPLE, REUSE, BATCH }
MyBatis提供了三種類型的Executor,分別為BatchExecutor,ReuseExecutor和SimpleExecutor,類圖如下所示。
在BaseExecutor中定義了四個抽象方法,如下所示。
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException; protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException; protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;
BaseExecutor提供的方法中會調用這四個抽象方法,同時這四個抽象方法需要由BatchExecutor,ReuseExecutor和SimpleExecutor根據(jù)各自的功能進行實現(xiàn),所以Executor的設計使用了模板設計模式。
第二件事情
,如果在MyBatis配置文件中開啟了二級緩存,則為Executor創(chuàng)建裝飾器CachingExecutor,以增加對二級緩存功能的支持。CachingExecutor與Executor的關系如下所示。
CachingExecutor實現(xiàn)了Executor接口,同時CachingExecutor持有Executor的引用,這是裝飾器模式的應用。關于MyBatis中的緩存,會在后續(xù)的文章中進行介紹。
第三件事情
,如果配置了插件,則將插件的邏輯添加到Executor中。關于MyBatis中的插件,會在后續(xù)的文章中進行介紹。
回到openSessionFromDataSource() 方法,創(chuàng)建好Executor后,就會調用DefaultSqlSession的構造方法創(chuàng)建一個DefaultSqlSession并返回,在DefaultSqlSession的構造方法中只是進行了簡單的賦值,如下所示。
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; }
至此,SqlSession就創(chuàng)建出來了。
總結
SqlSession的獲取是通過SqlSessionFactory的openSession() 方法,SqlSession的創(chuàng)建過程主要先是創(chuàng)建事務管理器,然后基于事務管理器創(chuàng)建執(zhí)行器Executor,最后基于Executor創(chuàng)建SqlSession。
通常,SqlSessionFactory為DefaultSqlSessionFactory,創(chuàng)建出來的SqlSession為DefaultSqlSession。
整體的一個流程圖如下所示。
以上就是詳解MyBatis的SqlSession獲取流程的詳細內容,更多關于MyBatis SqlSession獲取流程的資料請關注腳本之家其它相關文章!
相關文章
Mybatis中#{}和${}傳參的區(qū)別及#和$的區(qū)別小結
這篇文章主要介紹了Mybatis中#{}和${}傳參的區(qū)別及#和$的區(qū)別小結 的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07Spring?MVC異步上傳、跨服務器上傳和文件下載功能實現(xiàn)
這篇文章主要介紹了Spring?MVC異步上傳、跨服務器上傳和文件下載功能實現(xiàn),本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07java.net.MalformedURLException異常的解決方法
下面小編就為大家?guī)硪黄猨ava.net.MalformedURLException異常的解決方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05