MyBatis 源码分析——映射结果

MyBatis最后一步一定是处理相关的结果——把数据映射成对应的模型对象。事实上在笔者看来如果读者们了解了mybatis如何去执行数据库,又是如何处理数据结果。那么就了解了mybatis的主要路线。因为不管是什么样子的ORM最终都是要执行和处理结果的。而mybatis的亮点笔者也讲了——管理sql语句。所以相对而言,处理结果就显得十分的次要。但是笔者还是希望能在这里停留一下,研究他是如何处理结果。

mybatis处理结果的代码都在DefaultResultSetHandler类里面。很抱歉的是笔者去掉了程序是如何执行到DefaultResultSetHandler类的。这里面还是希望读者们自行的去跟踪一下。DefaultResultSetHandler类的handleResultSets方法便是笔者切入的代码。事实上当代码执行到handleResultSets方法的时候,已经执行了数据库。这在PreparedStatementHandler类里面就体现出来了。如下

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

用过mybatis的人都知道resultMap节点的作用。select节点的resultMap属性就是用来指点使用哪一个resultMap节点。而返回的结果也是用这一个节点来设置的。如下

<select id="SelectProducts" resultMap="result" >
        select * from Products
        where #{0} > ProductID and ProductName like #{1}
</select>
<resultMap type="com.aomi.vo.Product" id="result" autoMapping="true">
</resultMap>

所以我可以设想一下。当我们拿到了数据的时候,希望数据变成我们指定的对象模型。那么我们就必须知道是什么类型。一般来讲只要知道了什么类型就可以完成基本的映射条件。那么还会有可能出现一种希望自己去设计映射的字段和实例化对象。所以mybatis在这一方面也算是让笔者很喜欢。细详你可以查看一下官方的网站——XML映射文件

在映射的过程中,往往我们都需要知道表的字段。所以在DefaultResultSetHandler类的handleResultSets方法里面一开始就获得了关于表的信息。表的信息都存放在ResultSetWrapper类里面。后面便就是根据这些信息来处理每一行的数据。我们还是看一下源码吧。

 1 public List<Object> handleResultSets(Statement stmt) throws SQLException {
 2     ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 3 
 4     final List<Object> multipleResults = new ArrayList<Object>();
 5 
 6     int resultSetCount = 0;
 7     ResultSetWrapper rsw = getFirstResultSet(stmt);
 8 
 9     List<ResultMap> resultMaps = mappedStatement.getResultMaps();
10     int resultMapCount = resultMaps.size();
11     validateResultMapsCount(rsw, resultMapCount);
12     while (rsw != null && resultMapCount > resultSetCount) {
13       ResultMap resultMap = resultMaps.get(resultSetCount);
14       handleResultSet(rsw, resultMap, multipleResults, null);
15       rsw = getNextResultSet(stmt);
16       cleanUpAfterHandlingResultSet();
17       resultSetCount++;
18     }
19 
20     String[] resultSets = mappedStatement.getResultSets();
21     if (resultSets != null) {
22       while (rsw != null && resultSetCount < resultSets.length) {
23         ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
24         if (parentMapping != null) {
25           String nestedResultMapId = parentMapping.getNestedResultMapId();
26           ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
27           handleResultSet(rsw, resultMap, null, parentMapping);
28         }
29         rsw = getNextResultSet(stmt);
30         cleanUpAfterHandlingResultSet();
31         resultSetCount++;
32       }
33     }
34 
35     return collapseSingleResultList(multipleResults);
36   }

笔者标出切入需要的俩个代码段。一个就是获得表信息的,另一个就是用于处理每一行数据的。事实上面的multipleResults变量就能说明一个问题——可能实现多个select句语的执行。所以相对的reusltMap也变成了集合类型。在XML上面就是用逗号隔开的。笔者没有做过类似这种方式的代码。笔者也是从源码中看来的。他在MapperBuilderAssistant类的getStatementResultMaps方法里面。如下。

String[] resultMapNames = resultMap.split(",");

具体情况读者们可以自己去实验一下。

在笔者看来想要理解这一部分的关键点还在是getRowValue方法这边。这里面就是我们所说的处理结果。根据reusltMap节点的信息判断出是要自己根据类型生成对象。还是要通过reusltMap节点配置信息来生成对象。然后直行获值和设值。如下。

 1 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
 2     final ResultLoaderMap lazyLoader = new ResultLoaderMap();
 3     Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
 4     if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
 5       final MetaObject metaObject = configuration.newMetaObject(resultObject);
 6       boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
 7       if (shouldApplyAutomaticMappings(resultMap, false)) {
 8         foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
 9       }
10       foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
11       foundValues = lazyLoader.size() > 0 || foundValues;
12       resultObject = foundValues ? resultObject : null;
13       return resultObject;
14     }
15     return resultObject;
16   }

1.createResultObject方法:根据类型和配置信息生成对象。

2.applyAutomaticMappings方法:判断是否是autoMapping="true",如果是就是自动映射赋值。

3.applyPropertyMappings方法:根据配置信息赋值。

接下来,就是各位读者们根据自己的需要进行进一步的研究了。如果想知道是如何生成对象就是去看createResultObject方法。赋值的话就去看applyAutomaticMappings方法和applyPropertyMappings方法。

原文地址:https://www.cnblogs.com/hayasi/p/6617496.html