Mybatis—学习过程—Mybatis架构原理之executor执行原理
1.继续进行源码中selectList中的步骤,进入executor.query()
- 点击
query
方法,进入实现类BaseExecutor
//此方法在SimpleExecutor的父类BaseExecutor中实现 @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示 BoundSql boundSql = ms.getBoundSql(parameter); //为本次查询创建缓存的Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // 查询 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } @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()); // 判断执行器是否已经关闭,则抛出 ExecutorException 异常 if (closed) { throw new ExecutorException("Executor was closed."); } // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。 if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { // queryStack + 1 queryStack++; // Mybatis的一级缓存默认开启,根据刚才创建的catchKey从一级缓存中,获取查询结果 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 获取到,则进行处理 if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); // 获得不到,则从数据库中查询 } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { // queryStack - 1 queryStack--; } if (queryStack == 0) { // 执行延迟加载 for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 // 清空 deferredLoads deferredLoads.clear(); // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
- 点击
queryFromDatabase
进入方法,在一级缓存中没查到就到到数据库中查的方法// 从数据库中读取操作 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 执行读操作 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; }
- 点击
doQuery
进入SimpleExecutor
实现类中的doQuery
方法,可以看到执行器最终是将查询的操作过程交给了StatementHandler对象来处理@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(); // 传入参数创建StatementHanlder对象来执行查询 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建jdbc中的statement对象 stmt = prepareStatement(handler, ms.getStatementLog()); // 执行 StatementHandler ,进行读操作,到这一步的时候已经将查询任务交给StatementHandler了 return handler.query(stmt, resultHandler); } finally { // 关闭 StatementHandler 对象 closeStatement(stmt); } }
- 点击
prepareStatement
// 初始化 StatementHandler 对象 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 获得 Connection 对象 Connection connection = getConnection(statementLog); // 创建 Statement 或 PrepareStatement 预编译对象 stmt = handler.prepare(connection, transaction.getTimeout()); // 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符 handler.parameterize(stmt); //最终将预编译对象进行返回 return stmt; }
Mybatis架构原理之StatementHandler
1.StatementHandler主要完成两步
-
总结
就是参数的传递和结果集的转化
-
在
SimpleExecutor
实现类中的doQuery
方法中- 在执行
return handler.query(stmt, resultHandler);
这一步操作(进行读操作)
之前的一行代码stmt = prepareStatement(handler, ms.getStatementLog());
,执行了prepareStatement
方法,这个方法中的handler.parameterize(stmt);
这一步进行了参数设置
- 在执行
-
点击
parameterize
方法,进入PreparedStatementHandler
的parameterize
方法中@Override public void parameterize(Statement statement) throws SQLException { //使用ParameterHandler对象来完成对Statement的设值,所以说真正设置参数的还是parameterHandler对象 parameterHandler.setParameters((PreparedStatement) statement); }
-
点击
setParameters
方法,进入到实现类中,同时也说明了ParametorHandler
在设置参数的时候还要调用TypeHandler
,TypeHandler
设置了Java
的类型和jdbc
的数据类型转换,在代码的最后TypeHandler
也设置了?占位符的参数@SuppressWarnings("Duplicates") @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); // 遍历 ParameterMapping 数组 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { // 获得 ParameterMapping 对象 ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { // 获得值 Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // 获得 typeHandler、jdbcType 属性 TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } // 设置 ? 占位符的参数 try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
-
设置参数结束之后可以继续回到
SimpleExecutor
中的doQuery
方法- 上一步结束了参数的设置,接下来就要进行真正的读操作了,执行
return handler.query(stmt, resultHandler);
这一步了 - 点击
query
方法,找到实现类PreparedStatementHandler
中的query
方法@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 执行查询 ps.execute(); // 处理返回结果 return resultSetHandler.handleResultSets(ps); }
- 查看
handleResultSet
中的handleResultSets
是怎么执行的,点击handleResultSets
,找到实现类// // HANDLE RESULT SETS // // 处理 {@link java.sql.ResultSet} 结果集 @Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。 // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。 final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象 ResultSetWrapper rsw = getFirstResultSet(stmt); // 获得 ResultMap 数组 // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); // 校验 while (rsw != null && resultMapCount > resultSetCount) { // 获得 ResultMap 对象 ResultMap resultMap = resultMaps.get(resultSetCount); // 处理 ResultSet ,将结果添加到 multipleResults 中 handleResultSet(rsw, resultMap, multipleResults, null); // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象 rsw = getNextResultSet(stmt); // 清理 cleanUpAfterHandlingResultSet(); // resultSetCount ++ resultSetCount++; } // 因为 `mappedStatement.resultSets` 只在存储过程中使用,本系列暂时不考虑,忽略即可 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++; } } // 如果是 multipleResults 单元素,则取首元素返回 return collapseSingleResultList(multipleResults); }
- 上一步结束了参数的设置,接下来就要进行真正的读操作了,执行