MyBatis 源码分析-05 - MyBatis ResultSet 处理流程分析
限于篇幅, 本文仅解释resultSetHandler.handleResultSets()这一过程, 其他内容请参考其他文章.
Version :
MyBatis : 3.5.5
项目地址 :MyBatis-source-learn
- PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)
流程:
DefaultResultSetHandler.handleResultSets();
1. ResultSetWrapper rsw = getFirstResultSet(stmt);// 通过 Statement 获取resultSet,并包装
2. handleResultSet(rsw, resultMap, multipleResults, null); // 操作statement , 并关闭
1.handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);// 处理行数据 ,封装结果对象resultObject
1.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
1.Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 获取行数据
// 1. 实例化 resultObject, 根据class 创建 对应的类 (重要!!!)
1. Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
//2. 处理unMapped字段即自动注入. 将 JavaBean的属性 与 列属性 进行映射,并赋值 (重要!!!)
2. applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
// 1. 将列 与JavaBean属性 相绑定(columnName, property, typeHandler, propertyType)
1. autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
// 2.赋值.从列中取值,利用反射机制注入JavaBean对应的字段中即:metaObject.originObject(即rowValue中
2. metaObject.setValue(mapping.property, value);
// 3. 处理 mapped 字段. 将JavaBean属性与列属性 相互映射, 并赋值(过程与2 -- 大致相同,不再赘述)
3.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix)
2.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);//将rowValue加入到list中
测试代码:
ps :其他代码请自己去MyBatis-source-learn查看
测试类:
- CreateSqlSession.java
public class CreateSqlSession {
public static void main(String[] args) {
// 1. 连接数据库,配置基本参数
DataSource dataSource = getDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
// 2. 处理XXXMapper, 生成SqlSource(即处理xml 以及注解生成的SQL语句 与PreparedStatement绑定)
configuration.addMapper(BlogMapper.class);
// 3. 获取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 获取mapper,执行方法
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
for (int i = 0; i < 10; i++) {
Blog blog = mapper.selectBlog(1);
System.out.println("i: "+blog);
}
}
**PS: MyBatis 默认查询 顺序是 二级缓存 (CachedExecutor.cache) -> 一级缓存(BaseExecutor.localCache) -> 数据库. **
人话描述总流程:
1. 获取 resultSet 即 ResultSetWrapper rsw = getFirstResultSet(stmt);
2. 处理resultSet , nestedResultSet 即 handleResultSet(rsw, resultMap, multipleResults, null);
2.1 略过, 标签处理 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// 处理<>resultMap.Discriminate 接口
2.2 Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 获取行数据
2.2.1 实例化 ResultObject 即 createResultObject(rsw, resultMap, lazyLoader, columnPrefix)
2.2.2 赋值 ResultObject
1. unMapped 属性赋值 applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix)
1.1将列与JavaBean属性进行绑定 createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
1.2 获取列数据值 即 mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
1.3 利用反射将值注入 即 metaObject.setValue(mapping.property, value);
2. Mapped 属性赋值 applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix)
2.3 将rowValue 加入到list 中 即storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
3. 返回 list
源码分析:
- PreparedStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();//与数据库交互, 执行操作
return resultSetHandler.handleResultSets(ps);// 处理结果集 ,将结果集与 resultmap映射,返回list对象
}
方法执行流程堆栈信息:
说明: 下面我会给出整体流程 的代码, 会忽略部分无关重要的代码,可部分内容重要度不高,若不想看,可直接跳转到getRowValue(args)这个方法中,建议跳过, 若想看,建议速读.
整体流程 :
说明: 下面我会给出整体流程 的代码, 会忽略部分无关重要的代码,可部分内容重要度不高,若不想看,可直接跳转到getRowValue(args)这个方法中,建议跳过, 若想看,建议速读.
- DefaultResultSetHandler.java
handleResultSets(stmt)
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// 查询方法的返回值 list.
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 按照行依次处理 resultset ,
List<ResultMap> resultMaps = mappedStatement.getResultMaps();//resultMaps 在构建mappedStatement(SqlSource) 时生成, 通过addMappedStatement() 加入到resultmaps中
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount); //处理没有结果集的查询结果
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);// 将resultset 与 resultmap相绑定
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 处理嵌套resultset 即 nestedResultMap
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++;
}
}
- DefaultResultSetHandler.java
handleResultSet(args)
DefaultResultSetHandler.java
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
处理行数据
PS: 这一部分内容我会放较大部分比重, 这部分主要做了两件事,
1. **实例化resultObject , 即rowValue**
2. **赋值 将DB中的列与JavaBean的字段想绑定,并赋值**
3. 在赋值过程中 注意 MetaObject
getRowValue(args)
- DefaultResultSetHandler.java
//
// GET VALUE FROM ROW FOR SIMPLE RESULT MAP
//
// 实例化resultObject ,并且赋值 根据 resultSet 赋值
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);//1.创建 resultObject, 实例化但未初始化
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);// 元数据信息
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {//将类属性值与数据库查询结果 相绑定
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;//处理unmapped
}// 处理mapped property
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
实例化
时刻回忆总流程 Go!
实例化调用堆栈信息:
PS: 只会给出关键源码
createResultObject(args)
- DefaultResultSetHandler.java
// 实例化 and 构造映射
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();//构造器方法 参数类型集合
final List<Object> constructorArgs = new ArrayList<>(); // 构造器方法 参数集合
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
// 创建结果对象, 实例化 未初始化
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();// 结果的返回类型 xxxclass eg:Blog selectBlog()中resultType = Blog
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);//反射创建
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();// xml文件配置的<Constructor>
if (hasTypeHandlerForResultObject(rsw, resultType)) {// 处理基本数据类型
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {// 构造方法不为空
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {// 判断返回值类型是接口?返回值(类)是否有构造器
return objectFactory.create(resultType);// 利用ObjectFactory 反射的方式创建对应的class对象
} else if (shouldApplyAutomaticMappings(resultMap, false)) {// 是否需要自动映射
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
create(args)
- DefaultObjectFactory.java
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Class<?> classToCreate = resolveInterface(type);//1 处理集合接口
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);// 2. 实例化
}
instantiateClass(args)
DefaultObjectFactory.java
映射构造器方法 并 实例化类对象.
//类实例化 1. 无参构造 没有条件创造条件去执行 2. 有参构造
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;//1. 无参构造
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
try {
return constructor.newInstance();// 1.1 尝试调用默认的构造方法
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true); // 1.2 访问权限被拒 ,然后强制跳过访问权限生成实例, // TODO: 2021/3/13 应该抛异常, 不应该这么 处理
return constructor.newInstance();
} else {
throw e;
}
}
}//2. 处理带参构造器
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
try {
return constructor.newInstance(constructorArgs.toArray(new Object[0]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[0]));
} else {
throw e;
}
}
} catch (Exception e) {
String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
.stream().map(Class::getSimpleName).collect(Collectors.joining(","));
String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
.stream().map(String::valueOf).collect(Collectors.joining(","));
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
赋值
PS : 以AutoMapping赋值过程为例
赋值-堆栈调用过程
总方法:
applyAutomaticMappings(args)
- DefaultResultSetHandler.java
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 1. 获取 未映射的字段 以及与其对应的 JavaBean 属性 绑定的 列表
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {//2. 获取 列数据 值 ,即 value =1 (id =1 )
for (UnMappedColumnAutoMapping mapping : autoMapping) {//2.1 利用resultset(rs) 获取字段值rs.getString(name); //这里是将方法接口化,根据typeHandler 的类型来执行对应的方法getNullableResult()
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
} // 3. 通过反射的方式获取set方法,对metaObject中的originObject(rowValue 即Blog) 字段进行赋值
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
创建列与 JavaBean属性的映射
堆栈信息:
即 :
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
- DefaultResultSetHandler.java
// 将未配对的 列名 与 JavaBean 的属性 ,type 进行 配对,并将结果缓存处理
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) { //1.缓存中没有则创建
autoMapping = new ArrayList<>();
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName; // // 1.1 获取未配对的列名称, 并对列信息进行处理
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}//2 将列信息 与 JavaBean 字段相绑定, 不赋值
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {// row =blog metaObject = blog meta info
if (resultMap.getMappedProperties().contains(property)) {
continue;//2.1 判断 当前列是否已经映射过, 若是,则跳过
} //2.2 获取 与列 对应的 Java Bean 的属性名, 以及参数类型
final Class<?> propertyType = metaObject.getSetterType(property);// 返回property 的类型
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));//isPrimitive() 判断数据是否是原始数据类型
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}//3. 加入缓存
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
获取列数据值
for (UnMappedColumnAutoMapping mapping : autoMapping) {//2.1 利用resultset(rs) 获取字段值rs.getString(name); //这里是将方法接口化,根据typeHandler 的类型来执行对应的方法getNullableResult()
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
特殊说明:关于typeHandler接口的说明, 以StringTypeHandler 为例
typeHandler利用模板方法(父类定义过程,子类进行扩展)的设计模式, 也就是说:
typeHandler.getResult()-> BaseTypeHandler.getResult() -> XXXTypeHandler.getNullableResult() 的方式进行调用
属性赋值
- 调用堆栈信息
PS: 利用代理的方式获取JavaBean属性对应的setter方法, 然后实现赋值这一过程
特殊说明: metaObject.originObject =rowValue
setBeanProperty(prop,obj,val)
- BeanWrapper.java
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
try {
Invoker method = metaClass.getSetInvoker(prop.getName());// 获取setter 方法
Object[] params = {value};
try {
method.invoke(object, params);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (Throwable t) {
throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
}
}
总结:
- Mybatis 默认查询方法的执行流程是: 二级缓存 -> 一级缓存 -> DB
- Mybatis 生成实例对象 是利用 jDK 反射机制实现. 根据XXX.class 获取到Constructor, 去掉访问权限后调用newInstance()生成实例对象 . 传送门
- Mybatis 对象赋值的流程是 :
- 将类对象属性 与 数据表中列字段相匹配 [传送门](#创建列与 JavaBean属性的映射)
- 利用JDK反射机制生成setter方法, 对JavaBean进行属性注入 传送门
参考:
- https://tech.meituan.com/2018/01/19/mybatis-cache.html