• 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方法。

  • 相关阅读:
    Ubuntu18.04 Redis主从复制
    解决:git push error: failed to push some refs to
    手把手安装Laravel框架(permissions扩展包)实现RBAC权限---以及一些安装时的ERROR
    linux-Navicat 连接数据库 报错10060 & Navicat连接报错1146
    composer PHP Fatal error致命错误
    Yii框架基础增删查改
    cookie和session的区别
    mysql的索引优化
    什么是B+Tree
    螺旋矩阵
  • 原文地址:https://www.cnblogs.com/hayasi/p/6617496.html
Copyright © 2020-2023  润新知