zoukankan      html  css  js  c++  java
  • mybatis源码(四)-与JDBC交互

      之前研究了mybatis大体的执行流程,从上面SqlSession到Executor 内部。这里研究下其内部的处理, 也就是调用JDBC以及对结果出来部分。

      mybatis有几个重要的对象:ParameterHandler(java参数转为JDBC需要的参数处理器)、ResultSetHandler(负责将JDBC返回的ResultSet 结果集转换成List 类型的集合)、StatementHandler (封装JDBC Statement操作)。其获取都是在Configuration对象,方法如下:

      public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
      }
    
      public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
          ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
      }
    
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }
    
      public Executor newExecutor(Transaction transaction) {
        return newExecutor(transaction, defaultExecutorType);
      }
    
      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }

    1. 前置理解

      下面是个对象创建都是调用Configuration对象中API进行创建,会走mybatis的插件那一套机制。

    1. Executor 创建是在SqlSessionFactory类中openSession 方法创建SqlSession时会创建Executor

    2. StatementHandler 是在Executor中执行doUpdate或者doQuery 方法创建

    3. ParameterHandler 是在创建StatementHandler 过程中创建,对应方法在父构造org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler

    4. ResultSetHandler 是在创建StatementHandler 过程中创建,对应方法在父构造org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler

    2. 分析其过程

      以org.apache.ibatis.executor.SimpleExecutor#doQuery 执行查询为例子分析其参数设置过程以及结果处理过程。

    org.apache.ibatis.executor.SimpleExecutor#doQuery 源码如下:

      @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
      }

    1. org.apache.ibatis.session.Configuration#newStatementHandler 创建StatementHandler

      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }

    继续调用:org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandler

      public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    
      }

      这里是根据ms.getStatementType() 来获取,ms.getStatementType() 默认的类型是PREPARED。 我们也可以在xml写sql 的时候进行指定类型。所以会创建PreparedStatementHandler。

    最后会调用到:org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler (这里创建参数处理器和结果处理器)

      protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
    
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
    
        if (boundSql == null) { // issue #435, get the key before calculating the statement
          generateKeys(parameterObject);
          boundSql = mappedStatement.getBoundSql(parameterObject);
        }
    
        this.boundSql = boundSql;
    
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
      }

    2. 调用方法prepareStatement 获取Statement 对象

     1》handler.prepare(connection, transaction.getTimeout()); 准备一个Statement 对象, 会调用到:org.apache.ibatis.executor.statement.BaseStatementHandler#prepare

      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          statement = instantiateStatement(connection);
          setStatementTimeout(statement, transactionTimeout);
          setFetchSize(statement);
          return statement;
        } catch (SQLException e) {
          closeStatement(statement);
          throw e;
        } catch (Exception e) {
          closeStatement(statement);
          throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
      }

    instantiateStatement 调用到:org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement (这里实际就是创建PrepareStatement 对象)

      @Override
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
          String[] keyColumnNames = mappedStatement.getKeyColumns();
          if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
          } else {
            return connection.prepareStatement(sql, keyColumnNames);
          }
        } else if (mappedStatement.getResultSetType() != null) {
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          return connection.prepareStatement(sql);
        }
      }

    2》handler.parameterize(stmt); 参数化statement 对象。

    会调用到:org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize

      @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }

    紧接着调用到: com.baomidou.mybatisplus.MybatisDefaultParameterHandler#setParameters 

        @SuppressWarnings({"rawtypes", "unchecked"})
        @Override
        public void setParameters(PreparedStatement ps) {
            // 反射获取动态参数
            Map<String, Object> additionalParameters = null;
            try {
                additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
            } catch (IllegalAccessException e) {
                // ignored, Because it will never happen.
            }
            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);
                            if (value == null && MapUtils.isNotEmpty(additionalParameters)) {
                                // issue #138
                                value = additionalParameters.get(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);
                        }
                    }
                }
            }
        }

       这里核心逻辑是for循环遍历所有动态的参数,然后获取到参数的值value、类型处理器 TypeHandler、 JdbcType类型, 然后调用org.apache.ibatis.type.BaseTypeHandler#setParameter:(模板模式的使用)

      public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
          if (jdbcType == null) {
            throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
          }
          try {
            ps.setNull(i, jdbcType.TYPE_CODE);
          } catch (SQLException e) {
            throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                    "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                    "Cause: " + e, e);
          }
        } else {
          try {
            setNonNullParameter(ps, i, parameter, jdbcType);
          } catch (Exception e) {
            throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                    "Try setting a different JdbcType for this parameter or a different configuration property. " +
                    "Cause: " + e, e);
          }
        }
      }

      这个方法里面核心的设置参数交给子类实现 setNonNullParameter 方法,比如枚举类型参数的设置通过如下设置: org.apache.ibatis.type.EnumTypeHandler#setNonNullParameter(可以看到实际是设置了枚举类型的name() 方法)

      @Override
      public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
          ps.setString(i, parameter.name());
        } else {
          ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
        }
      }

    3. handler.<E>query(stmt, resultHandler); 进行处理然后处理结果

    1. 方法走到org.apache.ibatis.executor.statement.RoutingStatementHandler#query

      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.<E>query(statement, resultHandler);
      }

    2. 然后交给委托类org.apache.ibatis.executor.statement.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);
      }

    这里显示执行,然后交给resultSetHandler.<E> handleResultSets(ps); 处理执行结果。

    3.  调用到org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets处理回传结果:

    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
        final List<Object> multipleResults = new ArrayList<>();
    
        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++;
        }
    
        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 List<Object> collapseSingleResultList(List<Object> multipleResults) {
        return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
      }

    1》继续调用org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet 对结果进行处理:

      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());
        }
      }

      这里resultHandler 为null, 所以走第8行代码

    2》继续调用org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues

      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);
        }
      }

    3》 继续调用到:org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap

      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);
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }

    这里就是遍历每一行反向映射结果。rowValue 就是获取的结果。然后调用storeObject 将结果收集起来。

    3.1》 getRowValue 调用到 org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap)

      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
          final MetaObject metaObject = configuration.newMetaObject(rowValue);
          boolean foundValues = this.useConstructorMappings;
          if (shouldApplyAutomaticMappings(resultMap, false)) {
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
          }
          foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
      }

    这个方法内部createResultObject 是反射创建对象;

    对于rowValue 不为空,且mybatis环境中部包含类型处理器的,也就是类型不是Integer、Long 等类型的,会走 applyAutomaticMappings 是反射调用setter方法设置属性的值。如果是Integer、Long等基本类型的包装类型,!hasTypeHandlerForResultObject(rsw, resultMap.getType()) 返回是false。

    (1) createResultObject 反射创建对象逻辑如下:

    org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createResultObject

      private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        this.useConstructorMappings = false; // reset previous mapping result
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
        Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
          final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
          for (ResultMapping propertyMapping : propertyMappings) {
            // issue gcode #109 && issue #149
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
              resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
              break;
            }
          }
        }
        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
        return resultObject;
      }

    createResultObject 创建对象会调用到:

      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 (hasTypeHandlerForResultObject(rsw, 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);
      }

      这里根据不同的方式选择不同的创建对象的方法。

    第一种: 对于hasTypeHandlerForResultObject 返回为true的,可以认为是Integer,Long 等基本类型的包装类型的数据,会直接调用createPrimitiveResultObject

      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);
        }
        final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
        return typeHandler.getResult(rsw.getResultSet(), columnName);
      }

      可以看到实际就是genjuTypeHandler.getResult 获取返回结果。

    第二种: 是接口或者有默认构造方法的调用到:org.apache.ibatis.reflection.factory.DefaultObjectFactory#create(java.lang.Class<T>)

      @Override
      public <T> T create(Class<T> type) {
        return create(type, null, null);
      }
    
      public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Class<?> classToCreate = resolveInterface(type);
        // we know types are assignable
        return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
      }
    
      protected Class<?> resolveInterface(Class<?> type) {
        Class<?> classToCreate;
        if (type == List.class || type == Collection.class || type == Iterable.class) {
          classToCreate = ArrayList.class;
        } else if (type == Map.class) {
          classToCreate = HashMap.class;
        } else if (type == SortedSet.class) { // issue #510 Collections Support
          classToCreate = TreeSet.class;
        } else if (type == Set.class) {
          classToCreate = HashSet.class;
        } else {
          classToCreate = type;
        }
        return classToCreate;
      }
    
      private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
          Constructor<T> constructor;
          if (constructorArgTypes == null || constructorArgs == null) {
            constructor = type.getDeclaredConstructor();
            if (!constructor.isAccessible()) {
              constructor.setAccessible(true);
            }
            return constructor.newInstance();
          }
          constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
          if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
          }
          return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
        } catch (Exception e) {
          StringBuilder argTypes = new StringBuilder();
          if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
            for (Class<?> argType : constructorArgTypes) {
              argTypes.append(argType.getSimpleName());
              argTypes.append(",");
            }
            argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
          }
          StringBuilder argValues = new StringBuilder();
          if (constructorArgs != null && !constructorArgs.isEmpty()) {
            for (Object argValue : constructorArgs) {
              argValues.append(String.valueOf(argValue));
              argValues.append(",");
            }
            argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
          }
          throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
        }
      }

      resolveInterface(type); 先对类型进行重置,如果是Map,那么实际类型是HashMap;List或者集合实际类型是ArrayList等。这里需要注意如果返回类型是List<ExtensiveSubject>, 那么mybatis 实际需要的类型是ExtensiveSubject.class,不是List。然后根据不同的类型反射创建不同的对象。

    第三种:对于有指定构造方法的调用到org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createByConstructorSignature

      private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
                                                  String columnPrefix) throws SQLException {
        final Constructor<?>[] constructors = resultType.getDeclaredConstructors();
        final Constructor<?> annotatedConstructor = findAnnotatedConstructor(constructors);
        if (annotatedConstructor != null) {
          return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, annotatedConstructor);
        } else {
          for (Constructor<?> constructor : constructors) {
            if (allowedConstructor(constructor, rsw.getClassNames())) {
              return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix, constructor);
            }
          }
        }
        throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
      }
    
      private Constructor<?> findAnnotatedConstructor(final Constructor<?>[] constructors) {
        for (final Constructor<?> constructor : constructors) {
          if (constructor.isAnnotationPresent(AutomapConstructor.class)) {
            return constructor;
          }
        }
        return null;
      }
    
      private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) throws SQLException {
        boolean foundValues = false;
        for (int i = 0; i < constructor.getParameterTypes().length; i++) {
          Class<?> parameterType = constructor.getParameterTypes()[i];
          String columnName = rsw.getColumnNames().get(i);
          TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
          Object value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(columnName, columnPrefix));
          constructorArgTypes.add(parameterType);
          constructorArgs.add(value);
          foundValues = value != null || foundValues;
        }
        return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
      }
    
        。。。

      可以看到构造方法获取是:先获取AutomapConstructor 注解声明的构造,没有的情况下获取其他有参构造,然后获取到构造参数,遍历参数调用typeHandler.getResult 获取到实际的Java类型之后,再交给objectFactory.create 反射创建对象。机制和上面一样。

    (2) applyAutomaticMappings 反射调用setter 源码如下:(遍历映射,调用typeHandler.getResult 获取到值的结果,结果是java类型结果,然后反射修改属性值)

      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;
      }

    然后调用org.apache.ibatis.reflection.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);
        }
      }

    然后走 objectWrapper.set(prop, value); 调用到:org.apache.ibatis.reflection.wrapper.BeanWrapper#set

      public void set(PropertyTokenizer prop, Object value) {
        if (prop.getIndex() != null) {
          Object collection = resolveCollection(prop, object);
          setCollectionValue(prop, collection, value);
        } else {
          setBeanProperty(prop, object, value);
        }
      }

    走setBeanProperty, 这里需要注意。对于普通的Bean和Map、List等或有不同的策略。其继承关系图如下:

    第一种:org.apache.ibatis.reflection.wrapper.BeanWrapper#setBeanProperty

      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);
        }
      }

      可以看到这里就是获取到setter,然后反射调用setter 方法修改属性。

    第二种:org.apache.ibatis.reflection.wrapper.MapWrapper#set (可以看到如果返回值是Map类型,会走到这里进行put 操作)

      @Override
      public void set(PropertyTokenizer prop, Object value) {
        if (prop.getIndex() != null) {
          Object collection = resolveCollection(prop, map);
          setCollectionValue(prop, collection, value);
        } else {
          map.put(prop.getName(), value);
        }
      }

    补充: TypeHandler 是一个类型处理器。在mybatis也有重要的作用。在ParameterHandler 设置过程中,将Java类型转换为JDBC类型;在ResultSetHandler 设置过程中根据JDBC类型转换为Java类型。

    源码如下: org.apache.ibatis.type.TypeHandler

    public interface TypeHandler<T> {
    
      void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    
      T getResult(ResultSet rs, String columnName) throws SQLException;
    
      T getResult(ResultSet rs, int columnIndex) throws SQLException;
    
      T getResult(CallableStatement cs, int columnIndex) throws SQLException;
    
    }

      在ParameterHandler 参数设置时会调用setParameter 方法, 在ResultSetHandler 设置查询结果时会调用getResult 方法。可以看到就是根据Java的类型,然后进行处理。这里也是一个模板模式的使用,BaseTypeHandler 是一个抽象类,如下:

    public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    
      protected Configuration configuration;
    
      public void setConfiguration(Configuration c) {
        this.configuration = c;
      }
    
      @Override
      public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
          if (jdbcType == null) {
            throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
          }
          try {
            ps.setNull(i, jdbcType.TYPE_CODE);
          } catch (SQLException e) {
            throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                    "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                    "Cause: " + e, e);
          }
        } else {
          try {
            setNonNullParameter(ps, i, parameter, jdbcType);
          } catch (Exception e) {
            throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                    "Try setting a different JdbcType for this parameter or a different configuration property. " +
                    "Cause: " + e, e);
          }
        }
      }
    
      @Override
      public T getResult(ResultSet rs, String columnName) throws SQLException {
        T result;
        try {
          result = getNullableResult(rs, columnName);
        } catch (Exception e) {
          throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
        }
        if (rs.wasNull()) {
          return null;
        } else {
          return result;
        }
      }
    
      @Override
      public T getResult(ResultSet rs, int columnIndex) throws SQLException {
        T result;
        try {
          result = getNullableResult(rs, columnIndex);
        } catch (Exception e) {
          throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);
        }
        if (rs.wasNull()) {
          return null;
        } else {
          return result;
        }
      }
    
      @Override
      public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
        T result;
        try {
          result = getNullableResult(cs, columnIndex);
        } catch (Exception e) {
          throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);
        }
        if (cs.wasNull()) {
          return null;
        } else {
          return result;
        }
      }
    
      public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    
      public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
    
      public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
    
      public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
    
    }

    下面具体的Handler 只需要实现其抽象方法,比如对于枚举类型Handler 如下:

    package org.apache.ibatis.type;
    
    import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * @author Clinton Begin
     */
    public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
    
      private final Class<E> type;
    
      public EnumTypeHandler(Class<E> type) {
        if (type == null) {
          throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
      }
    
      @Override
      public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
          ps.setString(i, parameter.name());
        } else {
          ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
        }
      }
    
      @Override
      public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        return s == null ? null : Enum.valueOf(type, s);
      }
    
      @Override
      public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        return s == null ? null : Enum.valueOf(type, s);
      }
    
      @Override
      public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return s == null ? null : Enum.valueOf(type, s);
      }
    }

      可以看到枚举类型的参数赋值是调用enum.name() 方法; 反向数据库到JavaBean映射值是调用Enum.valueOf(type, s); 方法。

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    Win10 主题 美化 动漫
    Win10 主题 美化 动漫
    span 居中
    This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
    10 Future Web Trends 十大未来互联网趋势
    10 Future Web Trends 十大未来互联网趋势
    使用pycharm进行简单的数据库管理
    使用pycharm进行简单的数据库管理
    Python开发利器PyCharm 2.7附注册码
    Python开发利器PyCharm 2.7附注册码
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15426844.html
Copyright © 2011-2022 走看看