执行阶段总结
到这里,MyBatis的执行阶段从宏观角度看,一共完成了两件事:
- 代理对象的生成
- SQL的执行
而SQL的执行用了大量的篇幅来进行分析,虽然是根据一条查询语句的主线来进行分析的,但是这么看下来一定很乱,所以这里我会话一个流程图来帮助大家理解:
结果集处理
在SQL执行阶段,MyBatis已经完成了对数据的查询,那么现在还存在最后一个问题,那就是结果集处理,换句话来说,就是将结果集封装成对象。在不用框架的时候我们是使用循环获取结果集,然后通过getXXXX()方法一列一列地获取,这种方法劳动强度太大,看看MyBatis是如何解决的。
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); //resultMap可以通过多个标签指定多个值,所以存在多个结果集 final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; //拿到当前第一个结果集 ResultSetWrapper rsw = getFirstResultSet(stmt); //拿到所有的resultMap List<ResultMap> resultMaps = mappedStatement.getResultMaps(); //resultMap的数量 int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); //循环处理每一个结果集 while (rsw != null && resultMapCount > resultSetCount) { //开始封装结果集 list.get(index) 获取结果集 ResultMap resultMap = resultMaps.get(resultSetCount); //传入resultMap处理结果集 rsw 当前结果集(主线) handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } 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++; } } //如果只有一个结果集,那么从多结果集中取出第一个 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) { //判断resultHandler是否为空,如果为空建立一个默认的。 //结果集处理器 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()); } }
上文的代码,会创建一个处理结果集的对象,最终调用handleRwoValues()方法进行行数据的处理。
//处理行数据 public 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<>(); //获取当前结果集 ResultSet resultSet = rsw.getResultSet(); skipRows(resultSet, rowBounds); while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { //遍历结果集 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null); //拿到行数据,将行数据包装成一个Object Object rowValue = getRowValue(rsw, discriminatedResultMap, null); storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); } }
这里的代码主要是通过每行的结果集,然后将其直接封装成一个Object对象,那么关键
就是在于getRowValue()方法,如何让行数据变为Object对象的。
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { //创建一个空的Map存值 final ResultLoaderMap lazyLoader = new ResultLoaderMap(); //创建一个空对象装行数据 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())){ //通过反射操作返回值 //此时metaObject.originalObject = rowValue final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; if (shouldApplyAutomaticMappings(resultMap, false)) { //判断是否需要自动映射,默认自动映射,也可以通过resultMap节点上的autoMapping配置是否自动映射 //这里是自动映射的操作。 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; }
在getRowValue中会判断是否是自动映射的,我们这里没有使用ResultMap,所以是自动映射(默认),那么就进入applyAutomaticMappings()方法,而这个方法就会完成对象的封装。
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { //自动映射参数列表 List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); //是否找到了该列 boolean foundValues = false; if (!autoMapping.isEmpty()) { //遍历 for (UnMappedColumnAutoMapping mapping : autoMapping) { //通过列名获取值 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { //如果值不为空,说明找到了该列 foundValues = true; } 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; }
我们可以看到这个方法会通过遍历参数列表从而通过metaObject.setValue(mapping.property, value);
对返回对象进行赋值,但是返回对象有可能是Map,有可能是我们自定义的对象,会有什么区别呢?
实际上,所有的赋值操作在内部都是通过一个叫ObjectWrapper的对象完成的,我们可以进去看看它对于Map和自定义对象赋值的实现有什么区别,问题就迎刃而解了。
先看看上文中代码的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) { // 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()对结果进行赋值 objectWrapper.set(prop, value); } }
我们可以看看objectWrapper的实现类:
而我们今天举的例子,DemoMapper的返回值是Map,所以objectWrapper会调用MapWrapper的set方法,如果是自定义类型,那么就会调用BeanWrapper的set方法,下面看看两个类中的set方法有什么区别:
//MapWrapper的set方法 public void set(PropertyTokenizer prop, Object value) { if (prop.getIndex() != null) { Object collection = resolveCollection(prop, map); setCollectionValue(prop, collection, value); } else { //实际上就是调用了Map的put方法将属性名和属性值放入map中 map.put(prop.getName(), value); } } //BeanWrapper的set方法 public void set(PropertyTokenizer prop, Object value) { if (prop.getIndex() != null) { Object collection = resolveCollection(prop, object); setCollectionValue(prop, collection, value); } else { //在这里赋值,通过反射赋值,调用setXX()方法赋值 setBeanProperty(prop, object, value); } } 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); } } catch (Throwable t) { throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t); } }
上面两个set方法分别是MapWrapper和BeanWrapper的不同实现,MapWrapper的set方法实际上就是将属性名和属性值放到map的key和value中,而BeanWrapper则是使用了反射,调用了Bean的set方法,将值注入。
结语
至此,MyBatis的执行流程就为止了,本篇主要聊了从构建配置对象后,MyBatis是如何执行一条查询语句的,一级查询语句结束后是如何进行对结果集进行处理,映射为我们定义的数据类型的。
由于是源码分析文章,所以如果只是粗略的看,会显得有些乱,所以我还是再提供一张流程图,会比较好理解一些。
当然这张流程图里并没有涉及到关于回写缓存的内容,关于MyBatis的一级缓存、二级缓存相关的内容,我会在第三篇源码解析中阐述。
欢迎大家访问博客:http://blog.objectspace.cn
END