先来一张图,看看整个流程。。。。。
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);
}