Mybatis SqlSessionFactory與SqlSession詳細講解
SqlssionFactory
1.SqlSessionFactory是MyBatis的關(guān)鍵對象,它是個單個數(shù)據(jù)庫映射關(guān)系經(jīng)過編譯后的內(nèi)存鏡像。
2.SqlSessionFactory對象的實例可以通過SqlSessionFactoryBuilder對象類獲得,而SqlSessionFactoryBuilder則可以從XML配置文件或一個預(yù)先定制的Configuration的實例構(gòu)建出SqlSessionFactory的實例。
3.每一個MyBatis的應(yīng)用程序都以一個SqlSessionFactory對象的實例為核心。
4.SqlSessionFactory是線程安全的,SqlSessionFactory一旦被創(chuàng)建,應(yīng)該在應(yīng)用執(zhí)行期間都存在。在應(yīng)用運行期間不要重復(fù)創(chuàng)建多次,建議使用單例模式。
5.SqlSessionFactory是創(chuàng)建SqlSession的工廠。
SqlSessionFactory接口源碼如下所示
package org.apache.ibatis.session; import java.sql.Connection; public interface SqlSessionFactory { SqlSession openSession();//這個方法最經(jīng)常用,用來創(chuàng)建SqlSession對象。 SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); }
創(chuàng)建SqlSessionFactory
.mybatis框架主要是圍繞著SqlSessionFactory進行的,創(chuàng)建過程大概如下:
就從mybatis默認實現(xiàn)的MybatisAutoConfiguration來看看
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { //創(chuàng)建SqlSessionFactoryBean對象 SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); //設(shè)置數(shù)據(jù)源 factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { //mybatis ,Xml配置文件路徑 、factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } //應(yīng)用配置文件 applyConfiguration(factory); //Mybatis額外的配置文件 if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } //設(shè)置mybatis攔截器 if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } ··· //設(shè)置*mapper.xml掃描路徑 if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } ···· //獲取創(chuàng)建SqlSessionFactoryBean對象 return factory.getObject(); }
經(jīng)過一系列設(shè)置創(chuàng)建出SqlSessionFactory
注意僅有當(dāng)數(shù)據(jù)源只有一個實現(xiàn)時,MybatisAutoConfiguration才會生效,如果時多數(shù)據(jù)源的情況下,那么需要自己編寫定義SqlSessionFactory的創(chuàng)建邏輯
SqlSessionTemplate
SqlSessionTemplate是線程安全的,生命周期由spring管理的,同spring事務(wù)一起協(xié)作來保證真正執(zhí)行的SqlSession是在spring的事務(wù)中的一個SqlSession的實現(xiàn)類
創(chuàng)建SqlSessionTemplate
@Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } }
SqlSessionTemplate是作為一個bean被spring管理的
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }
內(nèi)部的sqlSessionProxy是對sqlSession做的代理
處理類是SqlSessionInterceptor
SqlSessionTemplate繼承自SqlSession,實現(xiàn)了sqlSession的所有操作
@Override public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } ·········
可以看出來,SqlSessionTemplate就是對SqlSession套了個殼子,具體實現(xiàn)還是有代理的sqlSessionProxy去執(zhí)行
接下類進入
SqlSessionInterceptor
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } }
先獲取一個sqlSession,這里也是sqlSession線程安全的原因
getSqlSession方法拿到DefaultSqlSession實例,getSqlSession方法里面處理了sqlSession的線程安全問題(通過ThreadLocal實現(xiàn))。
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } LOGGER.debug(() -> "Creating a new SqlSession"); session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
sqlSession與線程綁定,如果當(dāng)前線程未綁定sqlSession,會創(chuàng)建并綁定。保證了一個sqlSession一定只能被一個線程使用。
另外如果在一個事務(wù)下,那么肯定是在同一個線程,事務(wù)內(nèi)的操作會共用一個sqlSession,并且,每一步操作完畢之后不會自動提交事務(wù)。
SqlSession
1.SqlSession是MyBatis的關(guān)鍵對象,是執(zhí)行持久化操作的獨享,類似于JDBC中的Connection。
2.它是應(yīng)用程序與持久層之間執(zhí)行交互操作的一個單線程對象,也是MyBatis執(zhí)行持久化操作的關(guān)鍵對象。
3.SqlSession對象完全包含以數(shù)據(jù)庫為背景的所有執(zhí)行SQL操作的方法,它的底層封裝了JDBC連接,可以用SqlSession實例來直接執(zhí)行被映射的SQL語句。
4.每個線程都應(yīng)該有它自己的SqlSession實例。
5.SqlSession的實例不能被共享,同時SqlSession也是線程不安全的,絕對不能講SqlSeesion實例的引用放在一個類的靜態(tài)字段甚至是實例字段中。也絕不能將SqlSession實例的引用放在任何類型的管理范圍中,比如Servlet當(dāng)中的HttpSession對象中。
6.使用完SqlSeesion之后關(guān)閉Session很重要,應(yīng)該確保使用finally塊來關(guān)閉它。
package org.apache.ibatis.session; import java.io.Closeable; import java.sql.Connection; import java.util.List; import java.util.Map; import org.apache.ibatis.executor.BatchResult; public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); <E> List<E> selectList(String statement); <E> List<E> selectList(String statement, Object parameter); <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds); <K, V> Map<K, V> selectMap(String statement, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); void select(String statement, Object parameter, ResultHandler handler); void select(String statement, ResultHandler handler); void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); int insert(String statement); int insert(String statement, Object parameter); int update(String statement); int update(String statement, Object parameter); int delete(String statement); int delete(String statement, Object parameter); void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); List<BatchResult> flushStatements(); void close(); void clearCache(); Configuration getConfiguration(); <T> T getMapper(Class<T> type); Connection getConnection(); }
創(chuàng)建一個SqlSession
看了前面我們知道了sqlSession通過SqlSessionTemplate進行創(chuàng)建的
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); 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(); } }
創(chuàng)建一個事務(wù),創(chuàng)建一個Executor,構(gòu)造DefaultSqlSession
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; }
dirty字段代表如果在在未提交之前有更新操作,會被更新未true,這時調(diào)用sqlSession的commit/rollbacl方法
調(diào)用 executor.commit都傳的是true
@Override public void commit(boolean force) { try { executor.commit(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
后續(xù)sqlSession的增刪改查操作都通過executor完成,executor后面再繼續(xù)研究
SqlSession生命周期
如果說SqlSessionFactory相當(dāng)于數(shù)據(jù)庫連接池,那么SqlSession就相當(dāng)于一個數(shù)據(jù)庫連接(Connection對象),
你可以在一個事務(wù)里面執(zhí)行多條SQL,然后通過它的commit、rollback等方法,提交或者回滾事務(wù)。所以它應(yīng)該存活在一個業(yè)務(wù)請求中,
處理完整個請求后,應(yīng)該關(guān)閉這條連接,讓它歸還給SqlSessionFactory,否則數(shù)據(jù)庫資源就很快被消耗精光,系統(tǒng)應(yīng)付癱瘓,所以用try…catch…fanally語句來保證其正確關(guān)閉。
實際上MyBatis整合springBoot的情況下,SqlSession對象和線程進行綁定,對象本身可以循環(huán)被一個線程反復(fù)使用,但是依然保證了線程安全,在事務(wù)提交/回滾需要清理緩存,交換連接給數(shù)據(jù)庫連接池
到此這篇關(guān)于Mybatis SqlSessionFactory與SqlSession詳細講解的文章就介紹到這了,更多相關(guān)Mybatis SqlSessionFactory內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 解析Mybatis SqlSessionFactory初始化原理
- mybatis初始化SqlSessionFactory失敗的幾個原因分析
- MyBatis源碼解析——獲取SqlSessionFactory方式
- 使用Mybatis-Plus時的SqlSessionFactory問題及處理
- 詳解Mybatis核心類SqlSessionFactory的構(gòu)建
- Mybatis中自定義實例化SqlSessionFactoryBean問題
- MyBatis-plus報錯Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required的解決方法
- 使用Mybatis時SqlSessionFactory對象總是報空指針
相關(guān)文章
Java中快速排序優(yōu)化技巧之隨機取樣、三數(shù)取中和插入排序
快速排序是一種常用的基于比較的排序算法,下面這篇文章主要給大家介紹了關(guān)于Java中快速排序優(yōu)化技巧之隨機取樣、三數(shù)取中和插入排序的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-09-09Java Annotation注解相關(guān)原理代碼總結(jié)
這篇文章主要介紹了Java Annotation注解相關(guān)原理代碼總結(jié),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07Spring?Cloud?Alibaba?Nacos服務(wù)治理平臺服務(wù)注冊、RestTemplate實現(xiàn)微服務(wù)之間訪
這篇文章主要介紹了Spring?Cloud?Alibaba:Nacos服務(wù)治理平臺,服務(wù)注冊、RestTemplate實現(xiàn)微服務(wù)之間訪問,負載均衡訪問,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06Java 數(shù)據(jù)結(jié)構(gòu)算法Collection接口迭代器示例詳解
這篇文章主要為大家介紹了Java 數(shù)據(jù)結(jié)構(gòu)算法Collection接口迭代器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09