通过之前对 StatementHandler 类(Mybatis源码学习(五)——StatementHandler-CSDN博客)的分析发现在所有 StatementHandler 接口的实现类,无论是 PreparedStatementHandler、CallableStatementHandler 还是 SimpleStatementHandler 都是使用 ResultSetHandler 对象对结果(包括 List 列表、OUT 类型入参还是 Cursor 指针)进行处理,因此为了进一步分析查询结果的处理过程,本文将继续对 ResultSetHandler 及其相关类进行分析;
一 ResultSetHandler 接口
1 ResultSetHandler 对象的获取
在 mybatis 中 ResultSetHandler 对象的创建获取都是通过 Configuration 对象的 newResultSetHandler 方法进行创建,在方法的执行过程中首先创建 DefaultResultSetHandler 对象,然后使用 interceptorChain 属性插件链对创建的 DefaultResultSetHandler 对象进行装饰;
public class Configuration {
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);
return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
}
}
2 接口方法
ResultSetHandler 接口中共有三个方法,其中 handleResultSets 方法在 StatementHandler 对象的 query 方法中调用,用于解析普通查询结果列表;handleCursorResultSets 方法则是在 StatementHandler 对象的 queryCursor 方法中调用,用于对结果集产生的 Cursor 指针对象进行处理;handleOutputParameters 方法只在 CallableStatementHandler 对象调用,不仅在 query 与 queryCursor 查询方法,还在 update 更新方法进行调用。
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;
}
二 DefaultResultSetHandler 类
1 属性与创建过程
DefaultResultSetHandler 类的属性按照初始化和更新时间进行区分,一种是在初始化阶段进行初始化并且不会再对其进行修改,其中包含执行器 Executor 对象 executor、Configuration 配置对象 configuration、方法 MappedStatement 映射对象 mappedStatement、内部分页 RowBounds 对象 rowBounds,参数处理器 ParameterHandler 对象 parameterHandler、结果处理器对象 resultHandler、BoundSql 对象 boundSql、类处理器注册器 TypeHandlerRegistry 对象 typeHandlerRegistry,对象工厂 ObjectFactory 对象 objectFactory 及反射工厂 ReflectorFactory 对象 reflectorFactory;
public class DefaultResultSetHandler implements ResultSetHandler {
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;
}
第二种属性则是在对象初始化时赋空值,在方法执行过程中再进行填充;其中 nestedResultObjects 属性用于级联查询结果的复用,ancestorObjects 属性用于在级联 ResultMap 处理过程中保存父对象, previousRowValue 属性用于在级联查询中存储上次解析结果对象;nextResultMaps 与 pendingRelations 属性则是用于多 ResultSet 的解析;autoMappingsCache 属性用于缓存 ResultMap 中未映射的 UnMappedColumnAutoMapping 列表,key 为 ResultMap 的 id + 字段前缀;constructorAutoMappingColumns 属性缓存的则是构造方法使用的参数名列表,用于在自动映射中移除构造方法已赋值的参数列表;
public class DefaultResultSetHandler implements ResultSetHandler {
// 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<>();
private final Map<String, List<String>> constructorAutoMappingColumns = new HashMap<>();
// temporary marking flag that indicate using constructor mapping (use field to reduce memory usage)
private boolean useConstructorMappings;
}
2 handleResultSets 方法
2.1 方法流程分析
handleResultSets 方法首先通过 getFirstResultSet 方法封装执行结果 ResultSet 对象,然后再获取 mappedStatement 参数绑定的 ResultMap 对象列表同时调用 validateResultMapsCount 对 resultMap 的数量进行验证;在执行的 ResultSet 结果对象不为空时,循环对 ResultSet 结果对象进行遍历并对其进行处理直到将 ResultSet 结果处理数大于等于 ResultMap 对象列表数;之后遍历 mappedStatement 属性关联的 resultSet 字符串列表,然后对其进行解析;最后调用 collapseSingleResultList 获取实际执行结果;
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
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);
}
}
getFirstResultSet 方法遍历 stmt 参数的 ResultSet 对象直到获取第一个非空值,若最后都未获取到 ResultSet 对象直接放回 null,否则使用 ResultSetWrapper 对获取到的 ResultSet 对象进行封装并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
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)
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;
}
}
validateResultMapsCount 方法在 rsw 参数不为空但 resultMapCount 小于 1 时直接报错;
public class DefaultResultSetHandler implements ResultSetHandler {
private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
if (rsw != null && resultMapCount < 1) {
throw new ExecutorException(
"A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
+ "'. 'resultType' or 'resultMap' must be specified when there is no corresponding method.");
}
}
}
collapseSingleResultList 方法在 multipleResults 参数列表数为 1 时返回其第一个对象,否则则直接返回 multipleResults 参数;
public class DefaultResultSetHandler implements ResultSetHandler {
private List<Object> collapseSingleResultList(List<Object> multipleResults) {
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}
}
2.2 resultMaps 属性解析
在 resultMaps 遍历过程中,首先执行 handleResultSet 方法向 multipleResults 中添加参数,然后调用 getNextResultSet 方法获取下一个 ResultSet 对象并对其进行封装对 rsw 变量进行赋值,最后调用 cleanUpAfterHandlingResultSet 方法清空 nestedResultObjects 属性对象然后将 resultSetCount +1;
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
。。。
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
。。。
}
}
2.3 resultSets 属性解析
在 resultSets 遍历过程中,首先尝试获取 nextResultMaps 属性中 resultSet 字符串对应的 ResultMapping 对象,若存在对应的 ResultMapping 对象,则获取 ResultMapping 对象的关联 ResultMap 对象,最后使用 rsw 对象、关联 ResultMap 对象与对应的 ResultMapping 对象执行 handleResultSet 方法;之后调用 getNextResultSet 方法获取下一结果集,随后调用 cleanUpAfterHandlingResultSet 方法清空 nestedResultObjects 属性值;
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
。。。
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++;
}
。。。
}
}
getNextResultSet 方法在 stmt 参数支持多 ResultSet 对象且 stmt 参数拥有其他结果集时,尝试调用 getResultSet 方法获取其他 ResultSet 结果集对象;若获取到的结果集为空,则继续级联调用 getNextResultSet 方法并返回值,否则直接使用 ResultSetWrapper 封装结果集并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
private ResultSetWrapper getNextResultSet(Statement stmt) {
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
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) {
}
return null;
}
}
2.4 handleResultSet 方法
handleResultSet 方法在 parentMapping 参数不为空时直接调用 handleRowValues 方法对结果集进行处理,其中 ResultHandler 参数传空以及 RowBounds 参数传 DEFAULT 默认值;否则在 resultHandler 为空时,也是调用 handleRowValues 方法对结果集进行解析并保存到 defaultResultHandler 局部变量中,但 ResultHandler 参数传 DefaultResultHandler 对象, RowBounds 参数传 rowBounds 属性值及 parentMapping 参数传空,最后执行 defaultResultHandler 对象的 getResultList 方法并将处理完的结果对象添加到 multipleResults 参数中;否则使用 resultHandler 、rowBounds 属性与空 parentMapping 参数来执行 handleRowValues 方法;最后调用 closeResultSet 方法关闭 rsw 当前封装的 ResultSet 对象。
public class DefaultResultSetHandler implements ResultSetHandler {
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());
}
}
}
3 handleCursorResultSets 方法
3.1 方法流程分析
handleCursorResultSets 方法首先也是通过 getFirstResultSet 方法获取封装执行第一个 ResultSet 结果集对象,也会获取 mappedStatement 参数绑定的 ResultMap 对象列表同时调用 validateResultMapsCount 对 resultMap 的数量进行验证,最后使用本对象、resultMap 列表中的第一个元素、rsw 结果集封装对象以及 rowBounds 分页对象创建 DefaultCursor 对象并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
if (resultMapCount != 1) {
throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
}
ResultMap resultMap = resultMaps.get(0);
return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
}
}
3.2 DefaultCursor 对象
DefaultCursor 对象为 mybatis 中使用的 Cursor 指针对象,用于对结果集进行指针操作,为了能够更好的了解该对象,本节首先对其拥有的属性进行介绍;
3.2.1 属性总览
首先介绍在对象创建时初始化的参数,总共有四个,包含接口使用的 resultSetHandler 结果处理器对象、指针对象关联使用的 ResultMap 对象、ResultSet 返回结果集 ResultSetWrapper 包装对象以及 RowBounds 内部分页对象;除上述四个属性以外,还拥有指针结果处理器对象 ObjectWrapperResultHandler、指针迭代器 CursorIterator 对象、用于标识迭代器是否已返回的布尔类型属性 iteratorRetrieved、指针对象状态 status,其拥有 CREATED、OPEN、CLOSED 以及 CONSUMED 四种不同类型,在初始化时默认置为 CREATED,标识当前指针状态为已创建以及标识当前指针所在索引;
public class DefaultCursor<T> implements Cursor<T> {
// ResultSetHandler stuff
private final DefaultResultSetHandler resultSetHandler;
private final ResultMap resultMap;
private final ResultSetWrapper rsw;
private final RowBounds rowBounds;
protected final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
private final CursorIterator cursorIterator = new CursorIterator();
private boolean iteratorRetrieved;
private CursorStatus status = CursorStatus.CREATED;
private int indexWithRowBound = -1;
private enum CursorStatus {
/**
* A freshly created cursor, database ResultSet consuming has not started.
*/
CREATED,
/**
* A cursor currently in use, database ResultSet consuming has started.
*/
OPEN,
/**
* A closed cursor, not fully consumed.
*/
CLOSED,
/**
* A fully consumed cursor, a consumed cursor is always closed.
*/
CONSUMED
}
}
3.2.2 方法解析
DefaultCursor 对象中实现了 Cursor 接口中的所有方法:其中 isOpen 与 isConsumed 方法根据 status 值判断当前指针状态;getCurrentIndex 方法则是获取当前结果在所有结果中的索引;iterator 方法获取指针对象当前索引属性 cursorIterator,在方法执行过程中首先会对 iteratorRetrieved 属性与当前指针对象的对象进行验证,若 iteratorRetrieved 属性已经置位或当前指针已经被关闭了,则报错,验证通过后会将 iteratorRetrieved 置为 true 并返回 cursorIterator 属性对象;close 方法将 rsw 属性包装的 ResultSet 对象关闭的同时会将 status 值设置为 CLOSED,表示当前指针对象已被关闭。
public class DefaultCursor<T> implements Cursor<T> {
@Override
public boolean isOpen() {
return status == CursorStatus.OPEN;
}
@Override
public boolean isConsumed() {
return status == CursorStatus.CONSUMED;
}
@Override
public int getCurrentIndex() {
return rowBounds.getOffset() + cursorIterator.iteratorIndex;
}
@Override
public Iterator<T> iterator() {
if (iteratorRetrieved) {
throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
}
if (isClosed()) {
throw new IllegalStateException("A Cursor is already closed.");
}
iteratorRetrieved = true;
return cursorIterator;
}
@Override
public void close() {
if (isClosed()) {
return;
}
ResultSet rs = rsw.getResultSet();
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
// ignore
} finally {
status = CursorStatus.CLOSED;
}
}
}
3.2.3 ObjectWrapperResultHandler 内部类
ObjectWrapperResultHandler 类中拥有两个属性,result 属性用于存储当前结果对象,fetched 属性表示当前结果是否已经返;该类还拥有一个 handleResult 方法,且直接将 context 参数中的 resultObject 参数值赋值给 result 属性,同时调用 context 的 stop 方法并将 fetched 属性值置为 true。
public class DefaultCursor<T> implements Cursor<T> {
protected static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
protected T result;
protected boolean fetched;
@Override
public void handleResult(ResultContext<? extends T> context) {
this.result = context.getResultObject();
context.stop();
fetched = true;
}
}
}
3.2.4 CursorIterator 内部类
CursorIterator 内部类为 DefaultCursor 对象的迭代器,其中 object 属性用于存储当前关联结果对象,iteratorIndex 属性表示结果所在索引;
hasNext 方法在外部 objectWrapperResultHandler 属性中的 fetched 为 false 时,调用 DefaultCursor 对象的 fetchNextUsingRowBound 方法获取下一结果并将其赋值给 object 属性,最后返回 objectWrapperResultHandler 属性中的 fetched 值;
next 方法首先将局部变量 next 的值设置为 object 属性值,之后若外部 objectWrapperResultHandler 属性中的 fetched 为 false 时,调用 DefaultCursor 对象的 fetchNextUsingRowBound 方法获取下一结果并将其赋值给 next 局部变量,随后再次判断外部 objectWrapperResultHandler 属性中的 fetched 是否已置为 true,将外部 objectWrapperResultHandler 属性中的 fetched 置为 false,并将 object 属性置为 null 以及 iteratorIndex + 1 同时返回 next 局部变量值,否则抛出 NoSuchElementException 异常;
该迭代器不支持 remove 方法,执行时直接抛出 UnsupportedOperationException 异常;
public class DefaultCursor<T> implements Cursor<T> {
protected class CursorIterator implements Iterator<T> {
T object;
int iteratorIndex = -1;
@Override
public boolean hasNext() {
if (!objectWrapperResultHandler.fetched) {
object = fetchNextUsingRowBound();
}
return objectWrapperResultHandler.fetched;
}
@Override
public T next() {
T next = object;
if (!objectWrapperResultHandler.fetched) {
next = fetchNextUsingRowBound();
}
if (objectWrapperResultHandler.fetched) {
objectWrapperResultHandler.fetched = false;
object = null;
iteratorIndex++;
return next;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove element from Cursor");
}
}
}
3.2.5 fetchNextUsingRowBound 方法
fetchNextUsingRowBound 方法首先执行一次 fetchNextObjectFromDatabase 方法并将执行结果赋值给 result 局部变量,然后循环执行 fetchNextObjectFromDatabase 方法并将执行结果赋值给 result 局部变量直到 objectWrapperResultHandler 对象中的 fetched 属性为 false 或 indexWithRowBound 属性值大于等于 rowBounds 中的 offset 属性值后才会停止,最后返回 result 局部变量值;
public class DefaultCursor<T> implements Cursor<T> {
protected T fetchNextUsingRowBound() {
T result = fetchNextObjectFromDatabase();
while (objectWrapperResultHandler.fetched && indexWithRowBound < rowBounds.getOffset()) {
result = fetchNextObjectFromDatabase();
}
return result;
}
}
fetchNextObjectFromDatabase 方法首先判断当前指针对象是否已经关闭,已关闭后直接返回 null;之后分别将 objectWrapperResultHandler 属性的 fetched 值与 status 属性值设置为 false 与 OPEN 同时在 rsw 属性包装的 ResultSet 对象未关闭时调用 resultSetHandler 属性对象的 handleRowValues 方法解析当前结果并将其保存到 objectWrapperResultHandler 属性中,随后将 objectWrapperResultHandler 对象中保存的保存到 result 属性中的解析值赋值给 next 局部变量同时在 objectWrapperResultHandler 属性的 fetched 值被更新为 true 时将 indexWithRowBound 加 1,随后在 objectWrapperResultHandler 属性的 fetched 值被为 false 或 indexWithRowBound 属性值等于 rowBounds 属性中的 offset 与 limit 属性之和 - 1 时,关闭当前指针对象并将 status 属性值置为 CONSUMED,最后将 objectWrapperResultHandler 中的 result 属性置为 null并返回 next 局部变量值。
public class DefaultCursor<T> implements Cursor<T> {
protected T fetchNextObjectFromDatabase() {
if (isClosed()) {
return null;
}
try {
objectWrapperResultHandler.fetched = false;
status = CursorStatus.OPEN;
if (!rsw.getResultSet().isClosed()) {
resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
T next = objectWrapperResultHandler.result;
if (objectWrapperResultHandler.fetched) {
indexWithRowBound++;
}
// No more object or limit reached
if (!objectWrapperResultHandler.fetched || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
close();
status = CursorStatus.CONSUMED;
}
objectWrapperResultHandler.result = null;
return next;
}
}
4 handleOutputParameters 方法
4.1 方法流程分析
handleOutputParameters 方法首先方法实际入参对象同时使用 MetaObject 对参数进行封装并赋值给 metaParam 局部变量;随后获取 boundSql 中保存的 ParameterMapping 列表并对其进行遍历,在遍历的过程中,若 parameterMapping 的模式为 OUT 或 INOUT 时,根据参数类型执行对应逻辑,若类型为 ResultSet 时,调用 handleRefCursorOutputParameter 方法继续为参数赋值,否则使用参数对应的 TypeHandler 类型处理器对象中的 getResult 方法获取对应值为参数赋值;
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public void handleOutputParameters(CallableStatement cs) throws SQLException {
final Object parameterObject = parameterHandler.getParameterObject();
final MetaObject metaParam = configuration.newMetaObject(parameterObject);
final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {
final ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
if (ResultSet.class.equals(parameterMapping.getJavaType())) {
handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
} else {
final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
}
}
}
}
}
handleRefCursorOutputParameter 方法在 rs 参数为空时直接返回 null;随后若 resultHandler 属性为空,首先创建 DefaultResultHandler 对象并用其调用 handleRowValues 方法将值保存到该结果处理器中,随后使用结果处理器对象的 getResultList 方法获取保存值并对参数赋值,否则直接使用 handleRowValues 方法处理结果并执行 resultHandler 属性的 handleResult 方法对结果进行处理,但并不会返回值;
public class DefaultResultSetHandler implements ResultSetHandler {
private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam)
throws SQLException {
if (rs == null) {
return;
}
try {
final String resultMapId = parameterMapping.getResultMapId();
final ResultMap resultMap = configuration.getResultMap(resultMapId);
final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
if (this.resultHandler == null) {
final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rs);
}
}
}
5 handleRowValues 方法解析
handleRowValues 方法用于解析返回结果行并使用 resultHandler 对该值进行处理,其根据 resultMap 参数对象是否拥有级联 resultMap 来决定怎么继续处理对象;若拥有级联 resultMap 时,首先调用 ensureNoRowBounds 与 checkResultHandler 方法对 ResultHandler 与 RowBounds 属性进行验证,随后执行 handleRowValuesForNestedResultMap 方法,否则直接执行 handleRowValuesForSimpleResultMap 方法。
public class DefaultResultSetHandler implements ResultSetHandler {
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);
}
}
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.");
}
}
}
5.1 handleRowValuesForNestedResultMap 方法
handleRowValuesForNestedResultMap 方法首先调用 skipRows 方法跳过 ResultSet 结果对象中的不需要的结果同时使用 previousRowValue 属性值对 rowValue 局部变量进行赋值;然后在 ResultSet 参数需要进一步处理、resultSet 参数还未被关闭且 resultSet 参数还有结果时,首先调用 resolveDiscriminatedResultMap 获取 resultMap 的 DiscriminatedResultMap 对象,然后调用 createRowKey 方法创建 CacheKey 对象同时使用该对象从 nestedResultObjects 获取对应参数并将其赋值给 partialObject 局部变量;
接下来若 mappedStatement 属性中的 resultOrdered 值为 true 时,首先判断 partialObject 是否为空且 rowValue 是否不为空,若值为 true 时,首先会清空 nestedResultObjects 属性同时执行 storeObject 方法保存结果;然后统一执行 getRowValue 方法获取结果并将其赋给 rowValue 变量;若 mappedStatement 属性中的 resultOrdered 值不为 true 时,则是首先通过 getRowValue 方法获取结果并将其赋给 rowValue 变量,之后若 partialObject 为空会执行一次 storeObject 方法;
在完成上述循环后,若 rowValue 变量不为空、mappedStatement 属性中的 resultOrdered 值为 true 且结果还可以进一步处理时,首先会执行 storeObject 方法然后将 previousRowValue 值置为 null,否则在 rowValue 变量不为空时将 previousRowValue 值更新为 rowValue 变量值。
public class DefaultResultSetHandler implements ResultSetHandler {
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap,
ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
Object rowValue = previousRowValue;
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
Object partialObject = nestedResultObjects.get(rowKey);
// issue #577 && #542
if (mappedStatement.isResultOrdered()) {
if (partialObject == null && rowValue != null) {
nestedResultObjects.clear();
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
} else {
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
if (partialObject == null) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
}
if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
previousRowValue = null;
} else if (rowValue != null) {
previousRowValue = rowValue;
}
}
}
skipRows 方法在 rs 参数类型为 TYPE_FORWARD_ONLY 且 rowBounds 参数的 offset 属性值不为空时, 调用 rs 参数的 absolute 方法向后滚动 rowBounds 参数的 offset 值大小,否则在 rs 参数类型不为 TYPE_FORWARD_ONLY 时,则使用 rs 参数的 next 方法跳过 rowBounds 参数的 offset 值大小的结果量。
public class DefaultResultSetHandler implements ResultSetHandler {
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
rs.absolute(rowBounds.getOffset());
}
} else {
for (int i = 0; i < rowBounds.getOffset(); i++) {
if (!rs.next()) {
break;
}
}
}
}
}
5.2 handleRowValuesForSimpleResultMap 方法
handleRowValuesForSimpleResultMap 方法首先也是调用 skipRows 方法跳过 rowBounds 参数的 offset 值大小;然后在 ResultSet 参数需要进一步处理、resultSet 参数还未被关闭且 resultSet 参数还有结果时,在循环过程中首先调用 resolveDiscriminatedResultMap 方法获取 discriminatedResultMap 断言 ResultMap,然后执行 getRowValue 方法解析当前 rsw 对象绑定的结果,最后调用 storeObject 方法保存解析出来的结果值。
public class DefaultResultSetHandler implements ResultSetHandler {
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);
}
}
}
5.3 getRowValue 方法
getRowValue 方法用于创建单个结果源对象并对其属性赋值,DefaultResultSetHandler 类拥有两个不同的 getRowValue 方法,一个拥有 ResultSet 对象封装对象、resultMap 对象以及 columnPrefix 字段前缀参数三个入参,用于处理非级联 resultMap 值,在 handleRowValuesForSimpleResultMap 方法中调用;第二个 getRowValue 方法除了拥有上述三个参数以外还拥有 combinedKey 缓存对象与之前保存的 partialObject 参数,用于处理级联 resultMap 对象,是在 handleRowValuesForNestedResultMap 方法调用;
三个参数的 getRowValue 方法首先执行 createResultObject 方法创建结果对象并赋值给 rowValue 局部变量,随后若 rowValue 不为空且 resultMap 类型没有对应的 TypeHandler 类型处理器时首先使用 MetaObject 对 rowValue 对象进行封装,随后若 resultMap 参数应该进行自动映射时,调用 applyAutomaticMappings 方法自动映射进行赋值;随后调用 applyPropertyMappings 方法对属性进行赋值,最后若 returnInstanceForEmptyRow 或 rowValue 已经值已经成功设置时返回 rowValue 值,否则返回 null;
public class DefaultResultSetHandler implements ResultSetHandler {
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
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, columnPrefix) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
}
五参数的 getRowValue 方法在 partialObject 参数不为空时,首先使用 MetaObject 对其进行封装增强,随后将 rowValue 与 resultMapId 的映射关系添加 ancestorObjects 属性中,然后执行 applyNestedResultMappings 方法,在 applyNestedResultMappings 方法执行完成后移除 ancestorObjects 属性中的 rowValue 对应值;
在 partialObject 参数为空时,则首先 createResultObject 方法创建 rowValue 元对象,之后在 rowValue 不为空且 resultMap 类型没有对应的 TypeHandler 类型处理器与三参 getRowValue 方法中对应条件执行的逻辑一致,只是在调用 shouldApplyAutomaticMappings 判断是否可自动映射时第二个参数为 true 同时在 applyNestedResultMappings 方法执行后会移除 ancestorObjects 属性中的 rowValue 对应值,在上述逻辑执行完成后向 nestedResultObjects 中添加 combinedKey 参数与 rowValue 映射最后返回 rowValue 值;
public class DefaultResultSetHandler implements ResultSetHandler {
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix,
Object partialObject) throws SQLException {
final String resultMapId = resultMap.getId();
Object rowValue = partialObject;
if (rowValue != null) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
putAncestor(rowValue, resultMapId);
applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
ancestorObjects.remove(resultMapId);
} else {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, true)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
putAncestor(rowValue, resultMapId);
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true)
|| foundValues;
ancestorObjects.remove(resultMapId);
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
if (combinedKey != CacheKey.NULL_CACHE_KEY) {
nestedResultObjects.put(combinedKey, rowValue);
}
}
return rowValue;
}
}
5.3.1 createResultObject 方法
createResultObject 方法用于创建结果源对象(为对其属性赋值),其逻辑主要可以分为两部分,第一部分为创建空结果类型对象,但不会对其属性赋值,第二部分则是在对象需要进行延迟加载时,则使用 ProxyFactory 对创建的空结果对象进行代理用于实现属性的延迟加载;其中第一部分 resultType 结果类型对象的创建是使用另一个 createResultObject 方法实现的,同时会将创建使用的构造方法参数值与类型列表分别保存到 constructorArgs 与 constructorArgTypes 局部变量中,第二部分则是在满足成功创建对象、其拥有类型处理器、启用懒加载且 ResultMapping 列表中存在拥有关联 nestedQueryId 元素四个条件时,使用 proxyFactory 代理工厂对之前创建的 resultObject 对象进行代理用于懒加载代理;最后将 useConstructorMappings 属性值更新为当前是否使用了有参构造方法创建对象;
public class DefaultResultSetHandler implements ResultSetHandler {
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<>();
final List<Object> constructorArgs = new ArrayList<>();
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 方法在 resultType 结果类型存在对应结果处理器时直接执行 createPrimitiveResultObject 方法并返回,在 resultMap 的 constructorMappings 列表不为空时返回执行 createParameterizedResultObject 方法并返回,在 resultType 结果类型为接口或其拥有默认无参构造方法时则是利用 objectFactory 对象工厂创建创建对象并返回,最后在 resultMap 参数应该进行自动映射时,执行 createByConstructorSignature 方法并返回;若以上条件都无法满足时直接抛出异常;
public class DefaultResultSetHandler implements ResultSetHandler {
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);
}
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, resultMap, columnPrefix, resultType, constructorArgTypes,
constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
}
5.3.1.1 createPrimitiveResultObject 方法
createPrimitiveResultObject 方法中首先根据 resultMap 参数是否拥有关联 ResultMapping 对象列表;若拥有关联列表时,首先获取列表的第一个对象同时执行 prependPrefix 方法并将其结果赋值给 columnName 局部变量,否则使用 rsw 元数据中的第一个数据库字段名对 columnName 局部变量赋值;最后调用对应结果处理器的 getResult 方法并返回执行结果;
public class DefaultResultSetHandler implements ResultSetHandler {
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);
}
}
5.3.1.2 createParameterizedResultObject 方法
createParameterizedResultObject 方法使用 resultMMap 对象中设置的构造方法对结果对象进行创建;其首先遍历 constructorMappings 参数中的 ResultMapping 对象,若其存在关联的 nestedQueryId 属性时,使用 getNestedQueryConstructorValue 方法执行关联查询并使用其结果对 value 局部变量进行赋值,否则在关联 nestedResultMapId 属性不为空时,获取当前属性关联 ResultMap 对象然后调用 getRowValue 方法并将其结果赋值给 value 局部变量,在其他情况下则是调用对应的类型处理器的 getResult 方法来对 value 局部变量赋值;在完成 value 局部变量赋值之后将其添加到 constructorArgs 列表中同时向 constructorArgTypes 添加当前 ResultMapping 对象对应的类型 Class 对象;最后在完成 constructorMappings 参数的遍历完成后,使用 objectFactory 属性创建结果对象。
public class DefaultResultSetHandler implements ResultSetHandler {
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType,
List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs,
String columnPrefix) {
boolean foundValues = false;
for (ResultMapping constructorMapping : constructorMappings) {
final Class<?> parameterType = constructorMapping.getJavaType();
final String column = constructorMapping.getColumn();
final Object value;
try {
if (constructorMapping.getNestedQueryId() != null) {
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
} else if (constructorMapping.getNestedResultMapId() != null) {
String constructorColumnPrefix = getColumnPrefix(columnPrefix, constructorMapping);
final ResultMap resultMap = resolveDiscriminatedResultMap(rsw.getResultSet(),
configuration.getResultMap(constructorMapping.getNestedResultMapId()), constructorColumnPrefix);
value = getRowValue(rsw, resultMap, constructorColumnPrefix);
} else {
final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
}
} catch (ResultMapException | SQLException e) {
throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
}
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
}
5.3.1.3 createByConstructorSignature 方法
createByConstructorSignature 方法使用 resultMMap 对象中用 AutomapConstructor 注解标识的构造方法对结果对象进行创建;其直接调用 applyConstructorAutomapping 方法创建对象,其中关键的 Constructor 构造方法对象参数是通过 findConstructorForAutomapping 方法获取的, findConstructorForAutomapping 方法中对结果对象的所有构造方法进行遍历同时寻找其中使用 AutomapConstructor 注解修饰的构造方法,但 AutomapConstructor 注解修饰的构造方法只能有一个,若存在多个时直接抛出异常;若不存在构造方法使用 AutomapConstructor 注解修饰进行修饰同时 argNameBasedConstructorAutoMapping 配置为 true 时,将会随机选择一个构造器返回;
applyConstructorAutomapping 方法在 argNameBasedConstructorAutoMapping 配置为 true 时,使用 applyArgNameBasedConstructorAutoMapping 方法的执行结果对 foundValues 局部变量赋值,否则使用 applyColumnOrderBasedConstructorAutomapping 方法的执行结果对 foundValues 局部变量赋值,最后使用 objectFactory 参数创建结果对象并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
private Object createByConstructorSignature(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix,
Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
return applyConstructorAutomapping(rsw, resultMap, columnPrefix, resultType, constructorArgTypes, constructorArgs,
findConstructorForAutomapping(resultType, rsw).orElseThrow(() -> new ExecutorException(
"No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames())));
}
private Optional<Constructor<?>> findConstructorForAutomapping(final Class<?> resultType, ResultSetWrapper rsw) {
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
if (constructors.length == 1) {
return Optional.of(constructors[0]);
}
Optional<Constructor<?>> annotated = Arrays.stream(constructors)
.filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).reduce((x, y) -> {
throw new ExecutorException("@AutomapConstructor should be used in only one constructor.");
});
if (annotated.isPresent()) {
return annotated;
}
if (configuration.isArgNameBasedConstructorAutoMapping()) {
// Finding-best-match type implementation is possible,
// but using @AutomapConstructor seems sufficient.
throw new ExecutorException(MessageFormat.format(
"'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors.",
resultType.getName()));
} else {
return Arrays.stream(constructors).filter(x -> findUsableConstructorByArgTypes(x, rsw.getJdbcTypes())).findAny();
}
}
private Object applyConstructorAutomapping(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix,
Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor)
throws SQLException {
boolean foundValues = false;
if (configuration.isArgNameBasedConstructorAutoMapping()) {
foundValues = applyArgNameBasedConstructorAutoMapping(rsw, resultMap, columnPrefix, constructorArgTypes,
constructorArgs, constructor, foundValues);
} else {
foundValues = applyColumnOrderBasedConstructorAutomapping(rsw, constructorArgTypes, constructorArgs, constructor,
foundValues);
}
return foundValues || configuration.isReturnInstanceForEmptyRow()
? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
}
(1) applyArgNameBasedConstructorAutoMapping 方法
applyArgNameBasedConstructorAutoMapping 方法对使用的构造方法进行遍历,首先将参数的 Param 注解的 value 值或参数名赋值给 paramName 变量;若 rsw 中存在与 paramName 匹配的结果字段则通过对应的类型处理器获取结果,随后将结果添加到 constructorArgs 参数中时同时将 ResultMap 的 id + 字段前缀与rsw 参数关联字段名映射添加到 autoMappingsCache 属性中,最后若 rsw 参数没有与 paramName 匹配结果则将 paramName 值添加到 missingArgs 局部变量中,最后若 constructorArgs 大小小于 params 长度时直接报错,最后返回 foundValues 值;
public class DefaultResultSetHandler implements ResultSetHandler {
private boolean applyArgNameBasedConstructorAutoMapping(ResultSetWrapper rsw, ResultMap resultMap,
String columnPrefix, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor,
boolean foundValues) throws SQLException {
List<String> missingArgs = null;
Parameter[] params = constructor.getParameters();
for (Parameter param : params) {
boolean columnNotFound = true;
Param paramAnno = param.getAnnotation(Param.class);
String paramName = paramAnno == null ? param.getName() : paramAnno.value();
for (String columnName : rsw.getColumnNames()) {
if (columnMatchesParam(columnName, paramName, columnPrefix)) {
Class<?> paramType = param.getType();
TypeHandler<?> typeHandler = rsw.getTypeHandler(paramType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
constructorArgTypes.add(paramType);
constructorArgs.add(value);
final String mapKey = resultMap.getId() + ":" + columnPrefix;
if (!autoMappingsCache.containsKey(mapKey)) {
MapUtil.computeIfAbsent(constructorAutoMappingColumns, mapKey, k -> new ArrayList<>()).add(columnName);
}
columnNotFound = false;
foundValues = value != null || foundValues;
}
}
if (columnNotFound) {
if (missingArgs == null) {
missingArgs = new ArrayList<>();
}
missingArgs.add(paramName);
}
}
if (foundValues && constructorArgs.size() < params.length) {
throw new ExecutorException(MessageFormat.format(
"Constructor auto-mapping of ''{1}'' failed " + "because ''{0}'' were not found in the result set; "
+ "Available columns are ''{2}'' and mapUnderscoreToCamelCase is ''{3}''.",
missingArgs, constructor, rsw.getColumnNames(), configuration.isMapUnderscoreToCamelCase()));
}
return foundValues;
}
}
(2) applyColumnOrderBasedConstructorAutomapping 方法
applyColumnOrderBasedConstructorAutomapping 方法将 rsw 结果参数中前 constructor 构造对象参数中的形参个数个参数,并将对应的值和类型 Class 对象添加到 constructorArgs 与 constructorArgTypes 参数中,然后返回是否存在某些值为空;
public class DefaultResultSetHandler implements ResultSetHandler {
private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
String columnName = rsw.getColumnNames().get(i);
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
return foundValues;
}
}
5.3.2 属性自动映射
getRowValue 方法是使用 shouldApplyAutomaticMappings 方法判断是否启用自动映射,而应用自动映射则是通过 applyAutomaticMappings 方法进行实现;
5.3.2.1 shouldApplyAutomaticMappings 方法
shouldApplyAutomaticMappings 方法在 Mapping 设置了自动映射配置时直接返回该值,否则在 isNested 参数为 true 时返回 autoMappingBehavior 配置值是否为 FULL 否则返回自动映射配置值是否不为 NONE;
public class DefaultResultSetHandler implements ResultSetHandler {
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
if (resultMap.getAutoMapping() != null) {
return resultMap.getAutoMapping();
}
if (isNested) {
return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
} else {
return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
}
}
}
5.3.2.2 applyAutomaticMappings 方法
applyAutomaticMappings 方法使用 UnMappedColumnAutoMapping 对象列表设置属性自动映射值;该方法首先调用 createAutomaticMappings 方法创建 UnMappedColumnAutoMapping 对象列表随后对其进行遍历,逐一获取 ResultSet 对象返回的 mapping 对象的对应值并将其设置为 metaObject 的对应值;
public class DefaultResultSetHandler implements ResultSetHandler {
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;
}
}
createAutomaticMappings 方法首先尝试从 autoMappingsCache 缓存属性中获取之前缓存的 UnMappedColumnAutoMapping 对象列表,若之前迎接缓存了则直接返回;接下来首先调用 rsw 的 getUnmappedColumnNames 方法获取未在 resultMap 中设置的属性名列表,之后获取之前使用构造函数创建对象时缓存到 constructorAutoMappingColumns 属性中的构造方法中使用的参数名列表并将其从未映射字段列表中全部移除;接下来遍历剩下的未映射字段列表,之后若元数据中没有字段对应的 get 方法或不存在对应的 TypeHandler 类型处理器时,执行配置对象中设置的 autoMappingUnknownColumnBehavior 属性的 doAction 方法,默认什么都不做,否则创建 UnMappedColumnAutoMapping 对象并添加到 autoMapping 列表局部变量中;在遍历完成后将 mapKey 与 autoMapping 列表映射添加到 autoMappingsCache 属性中并返回 autoMapping 列表;
public class DefaultResultSetHandler implements ResultSetHandler {
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,
MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<>();
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
// Remove the entry to release the memory
List<String> mappedInConstructorAutoMapping = constructorAutoMappingColumns.remove(mapKey);
if (mappedInConstructorAutoMapping != null) {
unmappedColumnNames.removeAll(mappedInConstructorAutoMapping);
}
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (!columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
continue;
}
propertyName = columnName.substring(columnPrefix.length());
}
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping
.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property,
propertyType);
}
} else {
configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName,
property != null ? property : propertyName, null);
}
}
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
}
5.3.3 ResultMap 属性映射
ResultMap 属性映射值,即在上述自动映射过程中未完成映射的其余属性的设置的入口是 applyPropertyMappings 方法;
5.3.3.1 applyPropertyMappings 方法
applyPropertyMappings 方法首先会获取 ResultSet 结果集中所有 columnPrefix 参数为前缀的结果参数名列表;然后对 resultMap 参数关联 ResultMapping 对象列表进行遍历,若其 nestedResultMapId 不为空,则本方法中不对其进行处理;接下来调用 getPropertyMappingValue 方法获取对应结果值,随后若 propertyMapping 中 property 属性不为空时直接跳过本次循环,若获取的 value 结果值为 DEFERRED 时,将 foundValues 值赋为 true 并跳过这次循环,若 value 结果值不为空时,将 foundValues 值赋为 true,最后将对应结果属性值设置为 value;
public class DefaultResultSetHandler implements ResultSetHandler {
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
final Set<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);
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);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
}
if (value == DEFERRED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null
|| configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
}
5.3.3.2 getPropertyMappingValue 方法
getPropertyMappingValue 方法在 propertyMapping 参数关联 queryId 不为空时,执行 getNestedQueryMappingValue 方法并将返回其结果值;在 resultSet 属性不为空则执行 addPendingChildRelation 方法然后放回 DEFERRED ;否则执行关联类型处理器的 getResult 方法获取对应属性值并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
}
if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
return DEFERRED;
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}
}
5.3.3.3 getNestedQueryMappingValue 方法
getNestedQueryMappingValue 方法首先调用 prepareParameterForNestedQuery 方法获取级联查询请求参数对象,若其不为空时使用该参数获取级联查询的 BoundSql 对象,若执行器已经将级联查询结果缓存起来了则调用执行器的 deferLoad 方法加载缓存并将 value 局部变量值设置为 DEFERRED;否则创建 ResultLoader 对象,在启用延迟加载时则只是将 ResultLoader 对象与属性名映射保存到 lazyLoader 参数(之前在创建延迟加载代理对象时已经注入到代理对象中)中并返回 DEFERRED,否则直接调用 ResultLoader 对象的 loadResult 方法获取结果值并返回;
public class DefaultResultSetHandler implements ResultSetHandler {
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,
ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
final String nestedQueryId = propertyMapping.getNestedQueryId();
final String property = propertyMapping.getProperty();
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping,
nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT,
nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
if (executor.isCached(nestedQuery, key)) {
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
value = DEFERRED;
} else {
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery,
nestedQueryParameterObject, targetType, key, nestedBoundSql);
if (propertyMapping.isLazy()) {
lazyLoader.addLoader(property, metaResultObject, resultLoader);
value = DEFERRED;
} else {
value = resultLoader.loadResult();
}
}
}
return value;
}
}
5.3.3.4 addPendingChildRelation 方法
addPendingChildRelation 方法主要目的是利用参数创建 PendingRelation 对象中并将其添加到 pendingRelations 属性中以及将 parentMapping 参数的 resultSet 字符串与 parentMapping 参数值的映射保存到 nextResultMaps 属性中以供直接对 resultSet 字符串解析的过程中使用;
public class DefaultResultSetHandler implements ResultSetHandler {
private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping)
throws SQLException {
CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(),
parentMapping.getColumn());
PendingRelation deferLoad = new PendingRelation();
deferLoad.metaObject = metaResultObject;
deferLoad.propertyMapping = parentMapping;
List<PendingRelation> relations = MapUtil.computeIfAbsent(pendingRelations, cacheKey, k -> new ArrayList<>());
// issue #255
relations.add(deferLoad);
ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
if (previous == null) {
nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
} else if (!previous.equals(parentMapping)) {
throw new ExecutorException("Two different properties are mapped to the same resultSet");
}
}
}
5.3.4 级联 ResultMap 属性
拥有级联 ResultMap 对象的属性设置值的入口是 applyNestedResultMappings 方法;
5.3.4.1 applyNestedResultMappings 方法
applyNestedResultMappings 方法首先会对 resultMap 属性中的 propertyResultMappings 列表值,并筛选出其中关联了 ResultMap Id 但确没有设置 ResultSet 值的 ResultMapping 对象进行处理,在未设置 ResultMapping 的 columnPrefix 属性时若 ancestorObjects 属性中缓存了 nestedResultMapId 对应值且 newObject 参数为 fasle 时则直接跳过本次循环,在 newObject 参数为 true 时则调用 linkObjects 方法将 linkObjects 值设置为对应属性值;之后尝试从 nestedResultObjects 属性中获取通用缓存值之后解析级联查询值
public class DefaultResultSetHandler implements ResultSetHandler {
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,
String parentPrefix, CacheKey parentRowKey, boolean newObject) {
boolean foundValues = false;
for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
final String nestedResultMapId = resultMapping.getNestedResultMapId();
if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
try {
final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
if (resultMapping.getColumnPrefix() == null) {
// try to fill circular reference only when columnPrefix
// is not specified for the nested result map (issue #215)
Object ancestorObject = ancestorObjects.get(nestedResultMapId);
if (ancestorObject != null) {
if (newObject) {
linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
}
continue;
}
}
final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
if (rowValue != null && !knownValue) {
linkObjects(metaObject, resultMapping, rowValue);
foundValues = true;
}
}
} catch (SQLException e) {
throw new ExecutorException(
"Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
}
}
}
return foundValues;
}
}
5.4 结果保存方法
无论是否拥有级联 ResultMap 对象都会调用 storeObject 方法对解析结果进行保存;
5.4.1 storeObject 方法
storeObject 方法在 parentMapping 参数不为空时执行 linkToParents 方法,否则则是执行 callResultHandler 方法调用 ResultHandler 参数对结果进行处理;
public class DefaultResultSetHandler implements ResultSetHandler {
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue,
ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
callResultHandler(resultHandler, resultContext, rowValue);
}
}
}
5.4.2 linkToParents 方法
linkToParents 方法中首先使用 rs 参数、parentMapping 参数、parentMapping 中的 column 字段属性与 parentMapping 中的 foreignColumn 字段属性值来执行 createKeyForMultipleResults 方法创建 CacheKey 对象同时使用 CacheKey 对象从 pendingRelations 属性中获取 List<PendingRelation> 列表并赋值给 parents 局部变量;随后在 parents 不为空时对进行遍历,若 rowValue 值不为空时,执行 linkObjects 方法将 rowValue 值保存到父对属性中;
public class DefaultResultSetHandler implements ResultSetHandler {
private void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
CacheKey parentKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(),
parentMapping.getForeignColumn());
List<PendingRelation> parents = pendingRelations.get(parentKey);
if (parents != null) {
for (PendingRelation parent : parents) {
if (parent != null && rowValue != null) {
linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
}
}
}
}
}
5.4.2.1 linkObjects 方法
linkObjects 方法首先调用 instantiateCollectionPropertyIfAppropriate 方法判断 resultMapping 对应属性是否为集合,若为集合则返回属性值,同时在该集合属性为空时会进行初始化操作;之后在 instantiateCollectionPropertyIfAppropriate 方法执行结果不为空,即对应属性为集合类型时,则获取对应的属性对象值并将 rowValue 参数值添加到集合属性中,否则直接将对象属性值设置为 rowValue 参数值;
public class DefaultResultSetHandler implements ResultSetHandler {
private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
if (collectionProperty != null) {
final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
targetMetaObject.add(rowValue);
} else {
metaObject.setValue(resultMapping.getProperty(), rowValue);
}
}
}
5.4.3 callResultHandler 方法
callResultHandler 方法首先调用 DefaultResultContext 对象的 nextResultObject 方法对 resultCount 加 1 同时将 rowValue 参数值保存到 resultObject 属性中,之后调用 resultHandler 结果处理器参数的 handleResult 方法对 resultContext 参数进行处理;
public class DefaultResultSetHandler implements ResultSetHandler {
@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object> */)
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,
Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
}
三 ResultSetWrapper 类
ResultSetWrapper 类为 mybatis 使用的 ResultSet 结果包装类,其除了能够存储 ResultSet 对象以外,还提供很多包装的方法;
1 属性介绍及对象的创建
ResultSetWrapper 类属性可以分为三类,第一类是 ResultSet 对象及其衍生属性,其中 resultSet 属性为内部封装 ResultSet 对象、columnNames 属性为结果集字段名列表、jdbcTypes 为结果集字段数据库类型列表、classNames 为结果集字段全限定类名列表;第二类为 mybatis 关联属性,只有设置的类型处理器注册属性 typeHandlerRegistry;第三类为方法非通用属性,即在运行时根据方法调用进行更新的,其中 typeHandlerMap 为类型处理器缓存,最外层的 key 为字段名,内部 key 为类型对象,mappedColumnNamesMap 属性保存的是 ResultMap 映射字段,其中 key 为 ResultMap 对象 id + columnPrefix 参数,值则是字段名 set,最后一个为 unMappedColumnNamesMap ,其保存的未使用 ResultMap 定义的字段,其 key 值也是 ResultMap 对象 id + columnPrefix 参数,值则是字段名列表;
public class ResultSetWrapper {
private final ResultSet resultSet;
private final TypeHandlerRegistry typeHandlerRegistry;
private final List<String> columnNames = new ArrayList<>();
private final List<String> classNames = new ArrayList<>();
private final List<JdbcType> jdbcTypes = new ArrayList<>();
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
private final Map<String, Set<String>> mappedColumnNamesMap = new HashMap<>();
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
}
ResultSetWrapper 类只有一个构造方法,拥有 mybatis 配置对象与查询结果集 rs 两个参数;在对象创建过程中首先将 configuration 中的 typeHandlerRegistry 属性处理器注册对象属性值赋给 typeHandlerRegistry 属性以及查询结果 rs 参数赋值给 resultSet 属性;随后遍历 resultSet 结果中的元数据字段列表并分别将字段名或字段别名、jdbc 数据库字段类型与字段 java 类型全限定类名值添加到 columnNames 字段名列表、jdbcTypes 数据库字段类型列表及 classNames 字段全限定类名列表。
public class ResultSetWrapper {
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
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));
}
}
}
2 方法介绍
ResultSetWrapper 类拥有 8 个公开 get 方法,其中五个方法比较简单几乎都是直接获取对应属性,因此接下来将继续分析其余五个方法;
public class ResultSetWrapper {
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;
}
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;
}
}
2.1 getTypeHandler 方法
getTypeHandler 方法用于获取指定字段的类型处理器,方法首先尝试从 typeHandlerMap 中字段对应的获取对象类型与类型处理器映射 Map 同时之后尝试使用 propertyType 参数获取类型处理器对象,若获取到了直接返回该值;
否则则调用 getJdbcType 方法获取 columnName 参数对应的 jdbc 数据库字段类型同时使用其与 propertyType 参数从 typeHandlerRegistry 获取对应的类型处理器,若获取到的处理器为空或为 UnknownTypeHandler 对象时,则获取字段实际对应的数据类型 Class 对象并赋值给 javaType 局部变量,若 javaType 与 jdbcType 都不为空时通过 javaType 与 jdbcType 获取对应的结果处理器对象,否则在只有 javaType 不为空时直接使用 javaType 变量获取类型处理器,随后在只有 jdbcType 不为空时直接使用 jdbcType 变量获取类型处理器,若经过上述过程后 handler 还是为空或 UnknownTypeHandler 对象,则新建 ObjectTypeHandler 对象并赋值给 handler 变量,最后将 handler 值保存到 columnHandlers 缓存之后并返回其值。
public class ResultSetWrapper {
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;
}
private Class<?> resolveClass(String className) {
try {
// #699 className could be null
if (className != null) {
return Resources.classForName(className);
}
} catch (ClassNotFoundException e) {
// ignore
}
return null;
}
}
2.2 getMappedColumnNames 方法
getMappedColumnNames 方法用于获取当前包装 ResultSet 对象与在指定 ResultMap 存在映射的字段名 Set 集合;该方法首相尝试从 mappedColumnNamesMap 缓存中获取缓存的字段名 set,若未获取到则再调用 loadMappedAndUnmappedColumnNames 方法加载已映射字段与未映射字段,随后再次从 mappedColumnNamesMap 缓存中获取字段列表并返回该 set 值;
public class ResultSetWrapper {
public Set<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (mappedColumnNames == null) {
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return mappedColumnNames;
}
}
2.3 getUnmappedColumnNames 方法
getUnmappedColumnNames 方法执行逻辑与 getMappedColumnNames 方法相似,唯一的不同是该方法是从 unMappedColumnNamesMap 属性中获取当前包装 ResultSet 对象未在指定 ResultMap 存在映射的字段名 Set 集合。
public class ResultSetWrapper {
public Set<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (mappedColumnNames == null) {
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return mappedColumnNames;
}
}
2.4 loadMappedAndUnmappedColumnNames 方法
loadMappedAndUnmappedColumnNames 方法用于将 resultMap 参数中的映射字段与未映射字段加载分别加载到 mappedColumnNamesMap 与 unMappedColumnNamesMap 属性中;在该方法中首先调用 prependPrefixes 方法将 ResultMap 映射字段名列表统一加上前缀随后对 ResultSet 元数据的字段名列表进行遍历,若拥有前缀的 ResultMap 映射字段名列表包含当前被遍历的字段名时将其添加到 mappedColumnNamesMap 属性,否则添加到 unMappedColumnNamesMap 属性;
public class ResultSetWrapper {
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
Set<String> mappedColumnNames = new HashSet<>();
List<String> unmappedColumnNames = new ArrayList<>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
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);
}
}
prependPrefixes 方法只做了一件事,即对传入的 columnNames 参数进行遍历并将逐一为其添加 prefix 参数前缀并将其添加到新建的 prefixed 局部变量中,最后在遍历完成返回 prefixed 变量值;
public class ResultSetWrapper {
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;
}
}