最近想深入学习一下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;
}