先来一张图,看看整个流程。。。。。
1、使用Spring-Mybatis , SqlSessionTemplate 进行数据库操作
RowBounds row = new RowBounds(1,10);// 分页参数 List<Map<String, Object>> list = commonRepository.selectList("GradeMapper.getGrade", params,row); sqlSessionTemplate.selectList(statement, parameter,rowBounds); // 调用代理
2、public class DefaultSqlSession implements SqlSession ,通过代理可以调用默认使用DefaultSqlSession进行数据库操作,真正的操作又是委托给 Executor
@Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); 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(); } }
3、public class CachingExecutor implements Executor , 查询首先要做的就是判断缓存中是否存在,如果有就直接返回,否则就从数据中查询,这里的缓存是二级缓存
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); 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); }
4、public abstract class BaseExecutor implements Executor , 这里的缓存是一级缓存
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 { 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; }
6、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); stmt = prepareStatement(handler, ms.getStatementLog()); // 首先创建Statement,其次赋值Params return handler.<E>query(stmt, resultHandler); // 执行数据操作 } finally { closeStatement(stmt); // 关闭数据库连接 } }
7、stmt = prepareStatement(handler, ms.getStatementLog()); 创建Statement,其次赋值Params
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog);// 获取数据库连接 stmt = handler.prepare(connection); // 创建Statement handler.parameterize(stmt); // 赋值Params return stmt; }
1)prepare 设置他的超时以及批量数,以及选择 Statement 的形式
statement = instantiateStatement(connection); setStatementTimeout(statement); setFetchSize(statement); return statement;
2) 选择 Statement 形式,可滚动的结果集,mappedStatement.getResultSetType() 就是我们配置<select resultSetType> 属性
@Override protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { // 设置获取主键属性的值 String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() != null) { // 如果设置了滚动类型 return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { // 默认构建 return connection.prepareStatement(sql); } }
3)handler.parameterize(stmt) 设置参数,参数设置的重点在于参数类型的获取,也就是 JdbcType 的作用
@Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { 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 typeHandler = parameterMapping.getTypeHandler(); // 获取jdbcType,一般XML中不需要配置这个参数,系统可以自动匹配 JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { // 这里如果jdbcType=null,那么获取的就是UnknownTypeHandler typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
8、public class PreparedStatementHandler extends BaseStatementHandler 执行 Statement execute
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); // 执行sql语句 return resultSetHandler.<E> handleResultSets(ps); // 处理结果集 }
9、public class DefaultResultSetHandler implements ResultSetHandler 处理结果集
1)判断是否有嵌套的 ResultMap ,这个也是在 <select resultMap>设置的
private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { if (resultMap.hasNestedResultMaps()) { // 如果有嵌套 ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { // 如果没有嵌套 handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } }
2)假设没有嵌套的ResultMap ,那么进行结果集遍历,也就是Mybatis的分页
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>(); //首先跳过不需要的记录数 skipRows(rsw.getResultSet(), rowBounds); // 滚动结果集,直到 limit while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); //获取行中的数据 Object rowValue = getRowValue(rsw, discriminatedResultMap); // 解析值并存储 storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } }
3)skipRows(rsw.getResultSet(), rowBounds) 首先跳过不需要的记录数
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException { if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) { rs.absolute(rowBounds.getOffset()); } } else { // 没有设置滚动类型时默认就是 TYPE_FORWARD_ONLY ,这里就算是这个值,也可以使用 absolute ,为什么不用呢? for (int i = 0; i < rowBounds.getOffset(); i++) { rs.next(); } } }
4)shouldProcessMoreRows(resultContext, rowBounds) 是否已经到了Limit
private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException { return !context.isStopped() && context.getResultCount() < rowBounds.getLimit(); }
5)getRowValue(rsw, discriminatedResultMap) 从结果集的每一行中获取数据,同样这里也需要类型转换,ResultMap 中可以配置 JavaType JdbcType 对获取数据类型是有点作用的。
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null); // 获取类型转换器和以前一样 if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(resultObject); boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty(); if (shouldApplyAutomaticMappings(resultMap, false)) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; resultObject = foundValues ? resultObject : null; return resultObject; } return resultObject; }
6)storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()) 存储数据
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException { if (parentMapping != null) { linkToParents(rs, parentMapping, rowValue); } else { callResultHandler(resultHandler, resultContext, rowValue); }