因为机缘关注起来Mybatis,本文将探索Mybatis的整体执行流程,了解其大概是如何运作的。
Mybatis示例
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
SqlSession sqlSession = factory.openSession();
sqlSession.selectList("selectAllUser");
sqlSession.close();
一次Mybatis执行如上,前面三行代码应该就是扫描xml信息注册起来为后面提供使用,我们重点关注的是中间的那两行,它们是如何实现一次完整的查询的,上面的factory默认实现类为DefaultSqlSessionFactory。用此类来创建一个SqlSession,有关的SQL操作就是在SqlSession中执行的。
SqlSessionFactory
该接口的默认实现类是DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;//配置信息
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
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);
//实例化一个Executor,默认为SimpleExecutor,SqlSession就是使用Executor来执行SQL的。
final Executor executor = configuration.newExecutor(tx, execType);
//根据Executor生成一个SqlSession。
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();
}
}
}
SqlSession
该接口的默认实现类是DefaultSqlSession,上面生成的SqlSession就是该类的对象
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
//该executor为CachingExecutor,前文我们说了executor是用于执行SQL的,但是此处的executor并不是真正的执行器,只是一个静态代理执行器,mybatis的二级缓存就是在此实现的
private Executor executor;
private boolean autoCommit;//是否自动提交
private boolean dirty;
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从配置中获取到MappedStatement信息,MappedStatement封装了一条SQL相应的信息,
MappedStatement ms = configuration.getMappedStatement(statement);
//将MappedStatement交给代理执行器CachingExecutor执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
Executor
Mybatis的执行核心在Executor,它有好多个实现类,批量执行等就是在该接口中实现的,本文只关注SimpleExecutor整个实现类,也就是简单查询。
在调用真正的SimpleExecutor之前,Mybatis还调用了一个CachingExecutor。代码如下
public class CachingExecutor implements Executor {//缓存执行器,是一个代理的执行器。
//它的实例对象为SimpleExecutor,是真正执行器的实现类
private Executor delegate;
private TransactionalCacheManager tcm = new TransactionalCacheManager();
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//这就是CachingExecutor进一步要做的事,判断是否存在缓存,是则从缓存中获取,这是二级缓存。后文还有个个二级缓存
Cache cache = ms.getCache();//从ms获取缓存
if (cache != null) {//缓存不为空直接从缓存中返回
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//无缓存数据从数据库中查询返回
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
上面的分析中我们知道SQL的执行最终是由SimpleExecutor去执行的。来看下SimpleExecutor的结构
org.apache.ibatis.executor.Executor
org.apache.ibatis.executor.BaseExecutor
org.apache.ibatis.executor.SimpleExecutor
BaseExecutor是一个抽象类,实现了Executor接口
public abstract class BaseExecutor implements Executor {
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//是否存在缓存,注意此处的缓存不等于CacheExecutor的缓存,这个是一级缓存。
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {//不存在缓存则从数据库中查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
//从数据库中查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//注意此处,doQuery()方法是一个抽象方法,由子类去实现。
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
//抽象方法,也就是说具体的查询由子类去实现
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
}
再看子类SimpleExecutor,这个才是查询的真正实现类
public class SimpleExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//从配置文件中获取一个Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//注意参数stmt,将这个Statement传入到query()方法中。
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}
最后看SimpleStatementHandler是如何实现查询的。
public class SimpleStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();//生成要执行的SQL
statement.execute(sql);//这个就是真正实现查询的语句了,很熟悉吧,其实就是jdbc的内容了。
return resultSetHandler.<E>handleResultSets(statement);
}
}
本文为了快速了解整个Mybatis的运作流程,省略很多内容,如executor接口有很多的实现类,在此文中只给了一个。