主旨
当我们使用SqlSession调用数据库操作语句的时候,其内部处理流程是怎样的,类与类之间关系如何。
分析
在我们的springboot项目中,加入mybatis的使用是件容易的事情,如果没有特别深入的要求,只需在application.yml中添加极少的配置,便可直接在代码中通过注入SqlSession调用我们写在mapper文件中的sql。那么,当我们调用SqlSession中的selectList或selectOne方法时,mybatis究竟为我们做了些什么呢?
首先我们要知道,Mybatis通过自动配置的方式为我们减少了很多操作,我们需要找到一个关键类——MybatisAutoConfiguration。它位于mybatis-springboot-autoconfigure包下。把我们的目光放在SqlSessionTemplate这个方法上,点进去,发现它实现了SqlSession。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
注意sqlSessionProxy 这个变量,它采用了JDK动态代理,生成代理对象的第三个参数为其私有内部类SqlSessionInterceptor,这个类尤其重要,稍后会重新提及。
一般在我们使用时,会直接在Dao层添加SqlSession的引入,然后通过控制器自动注入:
public UserDao(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> findList(Map<String, Object> condition) {
return this.sqlSession.selectList("findList", condition);
}
这里的sqlSession变量的实例类为SqlSessionTemplate。
当一个请求过来的时候,比如调用selectList方法,会被引入到SqlSessionTemplate的selectList方法下,这时我们注意到,它又调用了sqlSessionProxy的同名方法:
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.selectList(statement);
}
注意了!!!
随后请求会进入到SqlSessionInterceptor类的invoke方法下:
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//重新获取一个sqlSession,它跟我们在Dao层中看的sqlSession并非同一个实例
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
看到invoke方法下又出现了一个sqlsession没有?它跟我们在Dao层看到的那个sqlSession可不一样。点进getSqlSession这个方法:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
// 重新获取一个sqlSession实例
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
可以看到它是重新从SqlSessionFactory获取的SqlSession实例,要想知道这个实例类是什么,需要点进openSession这个方法。或许你会疑问,SqlSessionFactory有两个实例类,分别是DefaultSqlSessionFactory和SqlSessionManager,该选择哪个?这里,我们使用的是DefaultSqlSessionFactory,因此你需要查看DefaultSqlSessionFactory里的openSession方法,如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
// invoke里sqlSession的实例类
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
OK,现在我们知道了invoke方法里的sqlSession的实例类是DefaultSqlSession,然后回过头去继续看invoke方法。当执行Object result = method.invoke(sqlSession, args);时,最终会执行到DefaultSqlSession下的同名selectList方法,即:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
网友评论