美文网首页
MyBatis印象阅读之ResultSetWrapper与par

MyBatis印象阅读之ResultSetWrapper与par

作者: 向光奔跑_ | 来源:发表于2019-08-15 11:35 被阅读0次

    在上一章内容中,我们介绍了ResultSetHandler的相关知识,但是又欠下了关于ResultSetWrapper封装数据库结果的类,我们先来整理下我们的技术债:

    parameterHandler
    ResultSetWrapper

    下面就来进入我们今天的正题。

    1.ResultSetWrapper解析

    我们先来看这个类的属性和构造方法:

    public class ResultSetWrapper {
      /**
       * ResultSet 对象
       */
      private final ResultSet resultSet;
      private final TypeHandlerRegistry typeHandlerRegistry;
      /**
       * 字段的名字的数组
       */
      private final List<String> columnNames = new ArrayList<>();
      /**
       * 字段的 Java Type 的数组
       */
      private final List<String> classNames = new ArrayList<>();
      /**
       * 字段的 JdbcType 的数组
       */
      private final List<JdbcType> jdbcTypes = new ArrayList<>();
      private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
      private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
      private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
    
      public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
        super();
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.resultSet = rs;
        // 遍历 ResultSetMetaData 的字段们,解析出 columnNames、jdbcTypes、classNames 属性
        final ResultSetMetaData metaData = rs.getMetaData();
        final int columnCount = metaData.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
          columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
          jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
          classNames.add(metaData.getColumnClassName(i));
        }
      }
    }
    

    这里就是通过ResultSet拿到数据库的列信息,然后进行分类。

    我们再来看一个这类的方法getTypeHandler:

     public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
        TypeHandler<?> handler = null;
        Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
        if (columnHandlers == null) {
          columnHandlers = new HashMap<>();
          typeHandlerMap.put(columnName, columnHandlers);
        } else {
          handler = columnHandlers.get(propertyType);
        }
        if (handler == null) {
          JdbcType jdbcType = getJdbcType(columnName);
          handler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
          // Replicate logic of UnknownTypeHandler#resolveTypeHandler
          // See issue #59 comment 10
          if (handler == null || handler instanceof UnknownTypeHandler) {
            final int index = columnNames.indexOf(columnName);
            final Class<?> javaType = resolveClass(classNames.get(index));
            if (javaType != null && jdbcType != null) {
              handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
            } else if (javaType != null) {
              handler = typeHandlerRegistry.getTypeHandler(javaType);
            } else if (jdbcType != null) {
              handler = typeHandlerRegistry.getTypeHandler(jdbcType);
            }
          }
          if (handler == null || handler instanceof UnknownTypeHandler) {
            handler = new ObjectTypeHandler();
          }
          columnHandlers.put(propertyType, handler);
        }
        return handler;
      }
    

    整体的逻辑就是把columnName的类型解析出来并缓存,如果没有的定义的话,使用jdbc对应的类型。

    我们再来看下一个类方法loadMappedAndUnmappedColumnNames:

      private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
        List<String> mappedColumnNames = new ArrayList<>();
        List<String> unmappedColumnNames = new ArrayList<>();
        final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
        // 拼接前缀 prefix
        final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
        for (String columnName : columnNames) {
          final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
          if (mappedColumns.contains(upperColumnName)) {
            mappedColumnNames.add(upperColumnName);
          } else {
            unmappedColumnNames.add(columnName);
          }
        }
        mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
        unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
      }
    

    这个方法比较眼熟,在我们上章映射数据结果时,如果没有开启自动映射,则就是调用是对应 mappedColumn不然就是unMappedColumn。

    这里的getMapKey和prependPrefixes方法也比较简单:

     private String getMapKey(ResultMap resultMap, String columnPrefix) {
        return resultMap.getId() + ":" + columnPrefix;
      }
    
    
      private Set<String> prependPrefixes(Set<String> columnNames, String prefix) {
        if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.length() == 0) {
          return columnNames;
        }
        final Set<String> prefixed = new HashSet<>();
        for (String columnName : columnNames) {
          prefixed.add(prefix + columnName);
        }
        return prefixed;
      }
    

    我们再来看对应的自动映射那个的方法:

      public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
        List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        if (mappedColumnNames == null) {
          loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
          mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        }
        return mappedColumnNames;
      }
    
      public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
        List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        if (unMappedColumnNames == null) {
          loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
          unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        }
        return unMappedColumnNames;
      }
    

    这里的逻辑也比较简单,大家自行阅读即可。

    接下来我们来分析parameterHandler的源码。

    2. ParameterHandler解析

    看这个类的名字,顾名思义就和参数的解析有关,我们来看下这个接口的源码:

    public interface ParameterHandler {
    
      Object getParameterObject();
    
      void setParameters(PreparedStatement ps)
          throws SQLException;
    
    }
    

    也比较简单,而且这个接口也只有一个实现类DefaultParameterHandler,这个类也不复杂,我们就整体来看下:

    public class DefaultParameterHandler implements ParameterHandler {
    
      private final TypeHandlerRegistry typeHandlerRegistry;
    
      private final MappedStatement mappedStatement;
      private final Object parameterObject;
      private final BoundSql boundSql;
      private final Configuration configuration;
    
      public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.configuration = mappedStatement.getConfiguration();
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
      }
    
      @Override
      public Object getParameterObject() {
        return parameterObject;
      }
    
      @Override
      public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty();
              if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) {
                value = null;
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                value = parameterObject;
              } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (TypeException | SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
    
    }
    
    

    这里看大致的意思就是PreparedStatement设置预编译的变量。

    3. 今日总结

    今天主要还是把欠了的技术债都还了。我们也总结下:

    • ResultSetWrapper是对数据返回的结果集进行信息整理,以便使用
    • parameterHandler主要是对PreparedStatement的参数注入。

    相关文章

      网友评论

          本文标题:MyBatis印象阅读之ResultSetWrapper与par

          本文链接:https://www.haomeiwen.com/subject/fejajctx.html