MyBatis 入门系列【25】 源码分析之 ResultSetHandler

89 阅读5分钟

1. 概述

之前说过在创建StatementHandler处理器时会同时创建ParameterHandlerResultSetHandler

ResultSetHandlerMybatis的核心组件之一,主要负责将结果集转化成结果列表和处理储存过程的输出。

2. 核心类

2.1 ResultSet

在原生JDBC查询的代码中,使用Statement进行操作,会返回ResultSet对象。

ResultSet也是java.sql中的接口,它表示通过执行查询数据库的语句生成的结果集对象,在JDBC的操作中数据库的所有查询记录将使用ResultSet进行接收。

		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try
		{
			Class.forName(driver);
			conn = DriverManager.getConnection(url,user,password);
			String sql = "SELECT sNo,sName,sex,age FROM "
					+ "Student WHERE dept = '计算机'";
			stmt = conn.createStatement();
			rs = stmt.executeQuery(sql);
			while(rs.next())
			{
				String no = rs.getString("sNo");
				String name = rs.getString("sName");
				String sex = rs.getString("sex");
				int age = rs.getInt("age");
				System.out.println(no + " " + name + " " + sex + "   " + age);
			}
		}

我们获取到ResultSet后,就可以将其转换为程序中的JAVA对象进行数据展示,但是原生的操作非常繁琐,所以Mybatis提供了ResultSet处理器,我们只需要定义好返回类型,Mybatis就可以自动进行转换映射了。

2.2 ResultSetHandler

ResultSetHandler是一个接口,也只有一个实现类,ResultSetHandler定义了一些处理结果集的方法。 在这里插入图片描述

ResultSetHandler接口源码:

/**
 * @author Clinton Begin
 * 结果集处理器
 */
public interface ResultSetHandler {

    //处理结果集
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;

    // 处理游标
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    //处理结果集
    void handleOutputParameters(CallableStatement cs) throws SQLException;

}

2.3 DefaultResultSetHandler

DefaultResultSetHandler是默认的结果集处理器,其中实现了很多处理一对一、一对多、嵌套查询等结果集的处理方法。

image.png

3. 执行流程

3.1 创建 ResultSetHandler

创建StatementHandler时,构造方法进入BaseStatementHandler,调用configuration的方法开始创建: 在这里插入图片描述 之后会调用DefaultResultSetHandler的构造方法,并且也使用了拦截器包装。 在这里插入图片描述 DefaultResultSetHandler构造方法中,也设置了执行器、configurationboundSql等重要参数,其中有一个reflectorFactory反射工厂,因为转为我们需要的对象是通过反射机制来创建的。

image.png

3.2 结果集处理

执行了查询之后,数据库的结果集就设置到了Statement对象中,接下来就是需要把Statement中的数据转换处理了。

首先调用DefaultResultSetHandlerhandleResultSets方法处理Statement

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);
     //根据resultMap处理rsw生成java对象
     handleResultSet(rsw, resultMap, multipleResults, null);
     //获取结果集的下一个结果
     rsw = getNextResultSet(stmt);
     cleanUpAfterHandlingResultSet();
     resultSetCount++;
   }

   String[] resultSets = mappedStatement.getResultSets();
   if (resultSets != null) {
   	//和resultMaps的遍历处理类似
     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);
 }

handleResultSets首先调用getFirstResultSet获取ResultSetWrapper

    // ResultSet包装
    private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
        // 获取ResultSet
        ResultSet rs = stmt.getResultSet();
        while (rs == null) {
            // move forward to get the first resultset in case the driver
            // doesn't return the resultset as the first result (HSQLDB 2.1)
            // 是否有多个ResultSet
            if (stmt.getMoreResults()) {
                rs = stmt.getResultSet();
            } else {
                if (stmt.getUpdateCount() == -1) {
                    // no more results. Must be no resultset
                    break;
                }
            }
        }
        // 包装
        return rs != null ? new ResultSetWrapper(rs, configuration) : null;
    }

调用ResultSetWrapper构造方法创建包装类,会封装当前查询返回的列名、数据类型等。


    public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
        super();
        // 类型处理器
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.resultSet = rs;
        // 元数据 ,表名、列名、数据库中数据类型等
        // com.mysql.cj.result.Field@668a32a4[dbName=angel_admin,tableName=base_user,originalTableName=base_user,columnName=user_id,originalColumnName=user_id,mysqlType=8(FIELD_TYPE_BIGINT),sqlType=-5,flags= AUTO_INCREMENT PRIMARY_KEY, charsetIndex=63, charsetName=ISO-8859-1]
        final ResultSetMetaData metaData = rs.getMetaData();
        // 列数 14
        final int columnCount = metaData.getColumnCount();
        // 循环所有列,从configuration配置中,获取对应信息
        for (int i = 1; i <= columnCount; i++) {
            // 列名集合
            columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
            // JDBC类型集合
            jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
            // 返回的数据类型集合
            classNames.add(metaData.getColumnClassName(i));
        }
    }

image.png 下一步通过mappedStatement获取我们配置的ResultMapResultMap包含了当前数据库查询结果和我们需要的Java对象之间的映射关系: image.png handleResultSets方法中,最重要的一步就是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());
        }
    }

handleResultSet方法中最重要的就是handleRowValues方法,该方法中,简单的列表查询会进入到handleRowValuesForSimpleResultMap方法,循环处理每一条ResultSet结果集:

  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

handleRowValuesForSimpleResultMap方法中使用getRowValue获取行数据,在该方法中又会调用createResultObject方法创建结果对象:

image.png

createResultObject进入到重载的另外一个createResultObject方法中:

	private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
		throws SQLException {
		// 返回的对象类型
		final Class<?> resultType = resultMap.getType();
		// Class 元数据
		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);
		}
		throw new ExecutorException("Do not know how to create an instance of " + resultType);
	}

这里进入到DefaultObjectFactory对象工厂的创建对象方法中,可以看到是通过反射机制调用构造函数实例化对象:

image.png 此时createResultObject方法返回的对象还没有进行属性设置: image.png 还需要getRowValue方法进行属性映射,最后返回了我们想要的结果: image.png