mybatis(一) sqlSessionFactory和sqlSession的建立
最近想深入學習一下mybatis,想通過看mybatis的原始碼,瞭解mybatis的整個工作流程,熟悉mybatis的各種細節。
使用mybatis的方式不同,sqlSessionFactory的建立方法也不同,具體可以看SqlSessionFactoryBuilder的原始碼,裡面有很多過載的build方法。本文是在SpringBoot環境下,基於mapper介面使用mybatis,在啟動專案中的某個時間點會呼叫SqlSessionFactoryBuilder中的如下方法建立SqlSessionFactory:
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
這個方法需要一個Configuration型別的引數,所以需要先建立Configuration物件,雖然不同的build方法實現有不同,但是核心都是解析mybatis.xml配置檔案和mapper.xml的配置檔案。
建立完sqlSessionFactory後,在具體執行增刪改查的時候,還需要建立sqlSession
在使用mapper介面增刪改查的方式中,sqlSession的建立是在執行增刪改查的過程中,在sqlSession的代理方法中建立的。
如下:sqlSessionTemplate會呼叫sqlSessionProxy的一個代理方法
public <E> List<E> selectList(String statement, Object parameter) { return this.sqlSessionProxy.<E> selectList(statement, parameter); }
sqlSessionProxy的建立方法如下,所以會呼叫SqlSessionInterceptor中的invoke方法
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()); }
SqlSessionInterceptor是SqlSessionTemplate中的一個private內部類, invoke方法中呼叫了
SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)
(由於直接匯入了靜態方法,所以呼叫的時候沒有在方法前面加上類名,import static org.mybatis.spring.SqlSessionUtils.getSqlSession;)
private class SqlSessionInterceptor implements InvocationHandler {
@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;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
sqlSessionUtils.getSqlSession()後面的時序圖如下:
在DefaultSqlSessionFactory的openSessionFromDataSource方法中,用 configuration.newExecutor(tx, execType)
建立了mybatis四大物件之一的executor物件,在執行增刪改查的時候需要用到executor
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();
}
}
newExecutor方法如下,executor = (Executor) interceptorChain.pluginAll(executor),這段程式碼將executor封裝成了攔截器鏈,是mybati的executor外掛的原理。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}