ResultSetHandler的主要作用是将数据库的结果集按照用户在配置文件中配置的映射关系组装成对应的java对象,这是mybatis的核心功能之一。
当执行完select查询语句之后,mybatis会将查询得到的结果集交给ResultSetHandler完成映射处理。
ResultSetHandler接口
ResultSetHandler接口的定义如下,其定义了三个方法,用于适配三种不同的场景:
public interface ResultSetHandler {
//映射结果集,得到E类型的对象集合
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
//映射结果集,得到游标对象
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
//处理存储过程的输出参数
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
ResultSetHandler接口只有一个默认实现,即DefaultResultSetHandler类,如果用户没有配置自己的ResultSetHandler实现类,则mybatis会使用DefaultResultSetHandler来处理查询语句的映射关系。
DefaultResultSetHandler
属性
private static final Object DEFERRED = new Object();
private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;
// nested resultmaps
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
private final Map<String, Object> ancestorObjects = new HashMap<>();
private Object previousRowValue;
// multiple resultsets
private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();
// Cached Automappings
private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
// temporary marking flag that indicate using constructor mapping (use field to reduce memory usage)
private boolean useConstructorMappings;
构造方法
对相关进行简单的初始化
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql,
RowBounds rowBounds) {
this.executor = executor;
this.configuration = mappedStatement.getConfiguration();
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.parameterHandler = parameterHandler;
this.boundSql = boundSql;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
this.reflectorFactory = configuration.getReflectorFactory();
this.resultHandler = resultHandler;
}
内部类
接口方法
handleResultSets方法用于处理从数据库中查询的结果集,并且可以处理CallableStatement调用存储过程所返回的结果集。
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);
//获取配置的ResultMap集合
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//校验配置的resultMaps大小
validateResultMapsCount(rsw, resultMapCount);
//迭代resultMaps集合逐一进行处理
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
//获取下一个结果集
rsw = getNextResultSet(stmt);
//清空nestedResultObjects集合
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);
}
其他方法
getFirstResultSet
该方法用于获取第一个结果集
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
if (stmt.getMoreResults()) {//是否还有待处理的结果集
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {//没有待处理的结果集
break;
}
}
}
//将结果集包装成ResultSetWrapper对象进行返回
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
getNextResultSet
该方法用于获取下一个结果集
private ResultSetWrapper getNextResultSet(Statement stmt) {
// Making this method tolerant of bad JDBC drivers
try {
//校验当前数据库连接是否支持多结果集
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
//如果存在多结果集,则将其封装成ResultSetWrapper对象返回
// Crazy Standard JDBC way of determining if there are more results
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
}
} catch (Exception e) {
// Intentionally ignored.
}
return null;
}
handleResultSet()
handleResultSets方法用于完成对多个ResultMap的映射,而handleResultSet方法则是用于完成对单个ResultMap的映射。
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) {
//如果用户未指定ResultHandler则使用默认的ResultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//映射结果集的核心代码
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {//使用用户定义的ResultHandler
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
//关闭结果集
closeResultSet(rsw.getResultSet());
}
}
handleRowValues
这里将结果集的映射分为两种情况进行处理,嵌套结果集映射和简单映射
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {//是否存在嵌套结果映射
//RowBounds配置校验
ensureNoRowBounds();
//ResultHandler校验
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {//简单映射
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
RowBounds和ResultHandler的校验
private void ensureNoRowBounds() {
if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
+ "Use safeRowBoundsEnabled=false setting to bypass this check.");
}
}
protected void checkResultHandler() {
if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
+ "Use safeResultHandlerEnabled=false setting to bypass this check "
+ "or ensure your statement returns ordered data and set resultOrdered=true on it.");
}
}
ResultSetWrapper类
ResultSetWrapper包装了ResultSet的元数据,并提供了一些辅助方法,便于操作结果集
属性
//持有的ResultSet对象
private final ResultSet resultSet;
//类型处理器注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
//结果集中每一列的列名
private final List<String> columnNames = new ArrayList<>();
//结果集中每一列对应的java类型
private final List<String> classNames = new ArrayList<>();
//结果集中每一列对应的JDBC类型
private final List<JdbcType> jdbcTypes = new ArrayList<>();
//列名 ------> (列的java类型------> 对应的类型处理器)
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
//resultMap.id+":"+columnPrefix ------> resultMap.getMappedColumns()中声明了映射关系的列名集合
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
//resultMap.id+":"+columnPrefix ------> resultMap.getMappedColumns()中未声明映射关系的列名集合
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
构造函数
构造函数中对columnNames、jdbcTypes和classNames属性进行了初始化
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
final ResultSetMetaData metaData = rs.getMetaData();
//获取结果集中总共有多少个列
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
//如果configuration.isUseColumnLabel()为真,则表示将使用 select nickname as name from user 中的name替代列名nickname
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
//添加jdbc类型
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
//添加该列对用的java类型
classNames.add(metaData.getColumnClassName(i));
}
}
其他方法比较简单,直接看注释
public ResultSet getResultSet() {
return resultSet;
}
public List<String> getColumnNames() {
return this.columnNames;
}
public List<String> getClassNames() {
return Collections.unmodifiableList(classNames);
}
public List<JdbcType> getJdbcTypes() {
return jdbcTypes;
}
//线性查找列对应的jdbc类型,
public JdbcType getJdbcType(String columnName) {
for (int i = 0; i < columnNames.size(); i++) {
if (columnNames.get(i).equalsIgnoreCase(columnName)) {
return jdbcTypes.get(i);
}
}
return null;
}
/**
* Gets the type handler to use when reading the result set.
* Tries to get from the TypeHandlerRegistry by searching for the property type.
* If not found it gets the column JDBC type and tries to get a handler for it.
*
* @param propertyType
* the property type
* @param columnName
* the column name
* @return the type handler
*/
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) {//表示第一次获取,需要初始化对应的TypeHandler
//获取列名对应的JdbcType
JdbcType jdbcType = getJdbcType(columnName);
handler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
// Replicate logic of UnknownTypeHandler#resolveTypeHandler
// See issue #59 comment 10
//如果handler为空,则表示需要对其类型进行推断,会根据情况通过javaType和jdbcType或javaType或jdbcType对其TypeHandler进行推断
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);
}
}
//如果还没获取到,那就使用兜底的ObjectTypeHandler作为其TypeHandler
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = new ObjectTypeHandler();
}
columnHandlers.put(propertyType, handler);
}
return handler;
}
private Class<?> resolveClass(String className) {
try {
// #699 className could be null
if (className != null) {
return Resources.classForName(className);
}
} catch (ClassNotFoundException e) {
// ignore
}
return null;
}
//初始化mappedColumnNamesMap和unMappedColumnNamesMap属性
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);
//为resultMap.getMappedColumns()集合的每一个元素都添加列前缀
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);
}
//获取resultMap对应的已映射的列集合,如果为空则对其进行初始化
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;
}
//获取resultMap对应的未映射的列集合,如果为空则对其进行初始化
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;
}
//拼接resultMap的key
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;
}
DefaultResultContext类
DefaultResultContext类实现自ResultContext接口,该类的主要作用是作为解析映射关系过程中的辅助类,后续分析简单映射及嵌套映射等场景的代码时会用到。
属性
//暂存映射后的结果对象
private T resultObject;
//记录resultObject的个数
private int resultCount;
//控制是否停止映射的标志位
private boolean stopped;
构造函数
public DefaultResultContext() {
resultObject = null;
resultCount = 0;
stopped = false;
}
接口方法
@Override
public T getResultObject() {
return resultObject;
}
@Override
public int getResultCount() {
return resultCount;
}
@Override
public boolean isStopped() {
return stopped;
}
public void nextResultObject(T resultObject) {
resultCount++;
this.resultObject = resultObject;
}
@Override
public void stop() {
this.stopped = true;
}
ResultContext接口
public interface ResultContext<T> {
T getResultObject();
int getResultCount();
boolean isStopped();
void stop();
}