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多線程編程之Synchronized關(guān)鍵字詳解
這篇文章主要為大家詳細介紹了java多線程編程之Synchronized關(guān)鍵字,感興趣的朋友可以參考一下2016-05-05
spring?boot集成WebSocket日志實時輸出到web頁面
這篇文章主要為大家介紹了spring?boot集成WebSocket日志實時輸出到web頁面展示的詳細操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03

