zoukankan      html  css  js  c++  java
  • Mybatis源码分析之--浅析ResultSetHandler

      本文浅析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);
          }

      

  • 相关阅读:
    主流浏览器默认限制的非安全端口号有哪些
    coco2dx实现翻拍效果
    iOS和android游戏纹理优化和内存优化(cocos2d-x)(转载)
    cocos2d-x如何解决图片显示模糊问题
    cocos2dx混合模式应用———制作新手引导高亮区域
    visual studio的项目属性表
    如何提高cocos2d-x-spine骨骼动画加载速度
    如何调试lua脚本
    把.pvr.ccz文件转换成png
    coco2dx加载网络图片并保存
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14042408.html
Copyright © 2011-2022 走看看