Mybatis是如何将结果封装成Java bean的
本篇文章是通过看视频学习总结的内容, 如有错误的地方请谅解,并联系博主及时修改,谢谢您的阅读.
源码地址: mybatis 中文注释版
前五篇博客地址:
Mybatis(五) - Mybatis是如何执行一条sql
Mybatis(四) - Mybatis是如何对Mapper接口进行代理的
Mybatis(三) - Mybatis是如何通过SqlSessionFactory得到SqlSession的
Mybatis(二) - Mybatis是如何创建出SqlSessionFactory的
Mybatis(一) - Mybatis 最原始是使用方式
前言: 前五篇文章中阐述了 mybatis
的初始化、创建 SqlSessionFactory
对象、得到 SqlSession
、动态代理 mapper
接口的方法、执行被代理的方法查询数据库,那么数据库查询出来的结果是一个 ResultSet
对象,如何将其转换为 Java Bean
的,这就要涉及到 Mybatis
中重要的对象 ResultSetHandler
本篇文章也是围绕着该对象进行开展的。
源码跟进
- 1.1 上一篇博客最后执行到了这里,如下代码:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 到了JDBC的流程
ps.execute();
// 处理结果集
return resultSetHandler.handleResultSets(ps);
}
- 1.1.1 这里重点执行的逻辑就是
resultSetHandler.handleResultSets(ps);
那么这也是接下来要分析的地方,进入方法。
@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);
}
- 1.1.2 进入到
handleResultSet()
方法中
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
// false
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
// true 进入这里
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());
}
}
- 1.1.3 进入方法
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null)
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// false
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
// true
else {
// 进入这里
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
- 1.1.4 进入方法
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
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);
// 重点关注这里,其实从这里执行完毕后已经获取到了 java bean 对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
- 1.1.5 继续进入
getRowValue(rsw, discriminatedResultMap, null)
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;
}
- 1.1.6 进入
applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix)
到达了终点,此时可以启用 debug 来调试一波了,根据注释上的编号,解释每一句话
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 1
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
// 2
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 3
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')
// 4
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
1:这一步,则是获取到查询出来的结果字段
column name
,放入UnMappedColumnAutoMapping
对象中进行存储
2:遍历整个集合,获取到每一个column name
3:既然拿到了column name
那么就可以直接通过column name
获取到对应的value
属性
4:拿到了name
属性和value
那么就可以变得不是那么复杂了,
- 1.1.7 继续进入第四步的方法
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
// false
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null) {
// don't instantiate child path if value is null
return;
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
}
// true
else {
// 当前对象的一个成员属性,这里存储,其他地方就会取出来封装到 java bean 中
objectWrapper.set(prop, value);
}
}
- 1.1.8 执行完毕后,回到
1.1.5
中,就会发现rowValue
变量就是我查询的User
对象,有图有真相
- 1.1.9 当
1.1.5
执行完毕后,回到1.1.4
将结果全部放入到DefaultResultContext<Object> resultContext
变量中,并且执行storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet)
, 把每一次返回的结果放入到 List 中,然后到1.1.3
再回到1.1.2
中,一旦这句代码执行后,handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
变量DefaultResultHandler defaultResultHandler
中list
个数就会增加到查询处正确结果的个数,请看图:
- 1.1.10 当执行完
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
后请看图:
结果就是4条,成功将数据返回,并且成功封装成了 Java Bean 对象
二、步骤分析
- 其实这也不是特别的难以理解,就是将结果处理完毕后,放入到上下文的返回结果中,然后当所有的数据都从 Statement 中取完毕的时候,直接从上下文中取出结果即可。
三、总结
- 从最初的例子,到封装成
Java Bean
拿到查询结果,我用了 六 篇博客来简单描述,其中包含了各种对象的初始化操作,参数的封装操作,sql 与 statementId 映射,sql 返回结果的映射,结果集的处理,得到 Java Bean,一系列繁琐的操作,都是Mybatis
帮我们完成的。回想起 JDBC 的那段时间,是苦不堪言的。阅读大佬的源码的同时,也需要做更多的笔记来加深对知识的消化,来提升自我。 - 以上所有内容(除源码外) 是由我手写的,如有错别字或者错误的地方请大佬们指正,相互学习才是成长的阶梯。