Mybatis解析xml文件
前面已经大概了解了Mybatis是怎样解析xml文件,这篇文章就来了解一下Mybatis执行sql整体过程。
SqlSession
SqlSession根据StatementId执行查询
String resource = "mybatis-config.xml";
Reader reader;
try {
//将XML配置文件构建为Configuration配置类
reader = Resources.getResourceAsReader(resource);
// 通过加载配置文件流构建一个SqlSessionFactory DefaultSqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
// 数据源 执行器 DefaultSqlSession
SqlSession session = sqlMapper.openSession();
try {
// SqlSession执行查询
User user = (User) session.selectOne("com.tuling.mapper.UserMapper.selectById", 1);
SqlSession只是一个门面模式,真正执行sql的是Executor
Executor有5种
SimpleExecutor:遇到insrt|update语句后,创建Statement对象,执行完毕后立即关闭Statement对象,只使用一次
BatchExecutor:遇到update语句后(JDBC只支持update批量操作),将所有的update语句添加到批处理中(addBatch()),多个Statement对象在addBatch()完后,等待执行executeBatch(),逐一执行
ReuseExecutor:遇到insrt|update语句后,根据SQL Id找Statement对象,如果有直接拿来用,如果没有就创建,然后放入Map<String,<Statement>>中,重复使用Statement对象
CachingExecutor(实现了二级缓存)
BaseExecutor(实现了一级缓存)
1.SqlSession对象在调用openSession()过程中创建Executor对象
/**
* 方法实现说明:从session中开启一个数据源
* @author:sxl
* @param execType:执行器类型
* @param level:隔离级别
* @return:SqlSession
* @exception:
* @date:2019/9/9 13:38
*/
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);
/**
* 创建一个sql执行器对象
* 一般情况下 若我们的mybaits的全局配置文件的cacheEnabled默认为ture就返回
* 一个cacheExecutor,若关闭的话返回的就是一个SimpleExecutor
*/
final Executor executor = configuration.newExecutor(tx, execType);
/**
* 创建返回一个DeaultSqlSessoin对象返回
*/
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();
}
}
2.plugin 使用责任链( interceptorChai)+代理模式用于增强4大核心对象,
Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler(getParameterObject,setParameter)
ResultSetHandler(handleResultSets,handleOutputParameters)
StatementHandler(prepare,parameterize,batch,update,query)
/**
* 方法实现说明:创建一个sql语句执行器对象
* @author:xsls
* @param transaction:事务
* @param executorType:执行器类型
* @return:Executor执行器对象
* @exception:
* @date:2019/9/9 13:59
*/
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 {
//简单的sql执行器对象
executor = new SimpleExecutor(this, transaction);
}
//判断mybatis的全局配置文件是否开启缓存
if (cacheEnabled) {
//把当前的简单的执行器包装成一个CachingExecutor
executor = new CachingExecutor(executor);
}
/**
* TODO:调用所有的拦截器对象plugin方法
*/
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
3.SqlSession.selectOne()调用selectList()
/**
* 方法实现说明
* @author:sxl
* @param statement: statementId
* @param parameter:参数对象
* @param rowBounds :mybiats的逻辑分页对象
* @return:
* @exception:
* @date:2019/9/9 20:33
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/**
* 第一步:通过我们的statement去我们的全局配置类中获取MappedStatement
*/
MappedStatement ms = configuration.getMappedStatement(statement);
/**
* 通过执行器去执行我们的sql对象
* 第一步:包装我们的集合类参数
* 第二步:一般情况下是executor为cacheExetory对象
*/
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();
}
}
4.Executor执行sql过程
// 如果开启了二级缓存会CacheExecutor执行query方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
/**
* 判断我们我们的mapper中是否开启了二级缓存<cache></cache>
*/
Cache cache = ms.getCache();
/**
* 判断是否配置了cache
*/
if (cache != null) {
//判断是否需要刷新缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
/**
* 先去二级缓存中获取
*/
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
/**
* 二级缓存中没有获取到
*/
if (list == null) {
//通过查询数据库去查询
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//加入到二级缓存中
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//没有整合二级缓存,直接去查询
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
5.SimpleExecutor执行doQuery(),这个过程中会创建PerpareStatementHandler对象
@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);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
6.PerpareStatement执行sql
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
7.ResultSetHandler处理返回值,返回数据
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
总结
1.SqlSession.openSession()创建了Executor对象(真正的sql执行者),这个对象有两大类普通Executor和CacheExecutor(实现二级缓存),BaseExecutor实现一级缓存。如果配置了插件,在创建Executor的过程中会创建Executor的代理。
2.Executor执行查询sql,从Cofiguration对象中获取MappedStatement(里面封装了sql信息),Executor由于是代理类,执行Plugin的invoke方法,做完中间处理之后,CacheExecutor先去二级缓存中找,如果找不到再调用BaseExecutor到一级缓存中找,还找不到就访问数据库,然后把数据放入一二级缓存,最终将数据返回。