本文浅析ResultSetHandler,说道浅析就是ResultSetHandler本身其实很复杂的,甚至可以说是mybatis四大组件最复杂的,因为resultMap本身还可以嵌套assosiation,collection的标签,一个mapper空间还可以有多个resultMap彼此之间还能嵌套。本文只讨论最一般的情况,就是一个ResultMap,或者一个ResultType的情况。
一 知识铺垫
在Mybatis解析配置文件时,会按照id进行缓存保存在 Configuration里
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
<resultMap id="BaseResultMap" type="me.gacl.domain.User" > <id column="user_id" property="userId" jdbcType="CHAR" /> <result column="user_name" property="userName" jdbcType="VARCHAR" /> <result column="user_birthday" property="userBirthday" jdbcType="VARCHAR" /> <result column="user_salary" property="userSalary" jdbcType="DOUBLE" /> </resultMap>
一个ResultMap大概张这个样子
org.apache.ibatis.mapping.ResultMap
public class ResultMap { private String id; //me.gacl.dao.UserMapper.BaseResultMap private Class<?> type; private List<ResultMapping> resultMappings;
//[ResultMapping{property='userId', column='user_id', javaType=class java.lang.String, jdbcType=CHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[ID], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userName', column='user_name', javaType=class java.lang.String, jdbcType=VARCHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userBirthday', column='user_birthday', javaType=class java.lang.String, jdbcType=VARCHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userSalary', column='user_salary', javaType=class java.lang.Double, jdbcType=DOUBLE, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}] private List<ResultMapping> idResultMappings; private List<ResultMapping> constructorResultMappings; private List<ResultMapping> propertyResultMappings;
//[ResultMapping{property='userId', column='user_id', javaType=class java.lang.String, jdbcType=CHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[ID], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userName', column='user_name', javaType=class java.lang.String, jdbcType=VARCHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userBirthday', column='user_birthday', javaType=class java.lang.String, jdbcType=VARCHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}, ResultMapping{property='userSalary', column='user_salary', javaType=class java.lang.Double, jdbcType=DOUBLE, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=false}] private Set<String> mappedColumns;//[USER_SALARY, USER_ID, USER_NAME, USER_BIRTHDAY] private Discriminator discriminator;//null private boolean hasNestedResultMaps; private boolean hasNestedQueries; private Boolean autoMapping;
二 代码入口
一个ResultSetHandler是随着一个StatementHandler的创建而创建的,这点其实好ParameterHandler是一样的
在StatementHandler执行之后对结果进行处理
PreparedStatementHandler.query
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
整个Mybatis对接口 ResultSetHandler的实现类只有一个 DefaultResultSetHandler
三 关键代码分析
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<Object>(); 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++; } //我们不分析resultMap嵌套的情况 String[] resultSets = mappedStatement.getResulSets(); 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); }
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()); }
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); } }
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); while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); Object rowValue = getRowValue(rsw, discriminatedResultMap);//经过这一步就是POJO对象了 storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } }
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); if (typeHandlerRegistry.hasTypeHandler(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);//走这个分支里,构造一个对象出来 } else if (shouldApplyAutomaticMappings(resultMap, false)) { return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }
最终会调用 DefaultResultSetHandler.applyPropertyMappings
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix); boolean foundValues = false; final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);// 表中的类名 user_id if (propertyMapping.getNestedResultMapId() != null) { // the user added a column attribute to a nested result map, ignore it column = null; } if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) || propertyMapping.getResultSet() != null) { Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);//这里就是jdbc的接口 // issue #541 make property optional final String property = propertyMapping.getProperty();//POJO里的属性 这里是 userId // issue #377, call setter on nulls if (value != DEFERED && property != null && (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()))) { metaObject.setValue(property, value);//通过反射对POJO赋值 } if (value != null || value == DEFERED) { foundValues = true; } } } return foundValues; }
getPropertyMappingValue方法中调用的方法就是下面的原理
StringTypeHandler.getNullableResult public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); }
======================2021-12-05===========================
我们再看 metaObject.setValue(property, value);//通过反射对POJO赋值 原理
MetaObject#setValue
public void setValue(String name, Object value) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { if (value == null && prop.getChildren() != null) { // don't instantiate child path if value is null return; } else { metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory); } } metaValue.setValue(prop.getChildren(), value); } else { objectWrapper.set(prop, value);//简单对象只需要看这个 } }
调用的是BeanWrapper#set方法
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) { try { Invoker method = metaClass.getSetInvoker(prop.getName()); Object[] params = {value}; try { method.invoke(object, params); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); }
可以看得出来,有set方法就调用set方法,没有就调用Field的方式,Field也是java反射的一种。
四 总结
1 如果用户用的不是ResultMap,而是ResultType怎么办?
public class ResultMap { private String id; //me.gacl.dao.UserMapper.selectName-Inline private Class<?> type; //class java.lang.String private List<ResultMapping> resultMappings; //[]都是空的 private List<ResultMapping> idResultMappings; private List<ResultMapping> constructorResultMappings; private List<ResultMapping> propertyResultMappings;//[] private Set<String> mappedColumns;//[] private Discriminator discriminator;//null private boolean hasNestedResultMaps; private boolean hasNestedQueries; private Boolean autoMapping;
当使用resultType时,Mybatis在解析xml的时候还是构造成ResultMap只不过属性和真实的ResultMap是不一样的
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); if (typeHandlerRegistry.hasTypeHandler(resultType)) { return createPrimitiveResultObject(rsw, resultMap, columnPrefix);//如果是ResultType走这个分支 } else if (!constructorMappings.isEmpty()) { return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix); } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { return objectFactory.create(resultType); } else if (shouldApplyAutomaticMappings(resultMap, false)) { return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }
private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final String columnName; if (!resultMap.getResultMappings().isEmpty()) { final List<ResultMapping> resultMappingList = resultMap.getResultMappings(); final ResultMapping mapping = resultMappingList.get(0); columnName = prependPrefix(mapping.getColumn(), columnPrefix); } else { columnName = rsw.getColumnNames().get(0);//user_name } final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName); return typeHandler.getResult(rsw.getResultSet(), columnName);//还是jdbc的东西 }
2 返回值是List 还是单个值 是怎么判断的
首先要说的是ResultSetHandler的返回值一定是一个List
解析xml并不能确定是返回list还是一个值,而是靠业务写的mapper接口也就是dao接口,这样在MapperMethod执行的时候是能够判断返回值类型是单值还是List,如果是单值则调用selectOne
if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); }