经过sql参数解析、sql动态组装和执行sql,相对而言,结果集的封装,是mybatis数据处理的最后一环。这里只对查询结果而言,因为更新语句一般都是返回影响的行数。抛开mybatis,如果让我们组装结果,我们该如何进行呢?mybatis的查询结果统一表示为:
List<E>
即使是查询单个对象,它的查询结果还是封装成 List 对象,然后返回list集合的第一个元素。
个人根据mybatis的源码,将mybatis对结果集的封装,分成两步:
下面开始进入主题:
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="isValid" column="is_valid"></result>
<collection property="blogs" javaType="java.util.List" ofType="Blog">
<id property="id" column="blog_id"></id>
<result property="title" column="title"></result>
<result property="userId" column="user_id"></result>
</collection>
</resultMap>
<select id="getUserAndBlogByUserId" parameterType="string" resultMap="userMap">
select u.id,u.username,u.password,u.is_valid,b.id as blog_id,b.title,b.user_id
from t_user u LEFT JOIN t_blog b ON u.id = b.user_id
where u.id=#{id}
</select>
从查询sql,你也可以发现一二,比如博客表t_blog中,含有一个逻辑外键user_id,表示该博客属于哪个用户的,而每个用户可以拥有多个博客,显然是一对多的关系,而查询条件则为用户id。
2.实体类
public class User implements Serializable{
private String id;
private String username;
private String password;
private Integer isValid;
//一个用户,对应多篇博客
private List<Blog> blogs;
}
public class Blog implements Serializable{
private String id;
private String title;
private String userId;
}
3.mapper接口
public interface UserMapper {
//根据id查询用户及其所有博客
User getUserAndBlogByUserId(String id);
}
4.测试方法
public class One2ManyQuery {
public static void main(String[] args) throws IOException {
//读取配置信息
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//根据配置信息,创建SqlSession工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//SqlSession工厂创建SqlSession
SqlSession sqlSession = factory.openSession();
//获取接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserAndBlogByUserId("123");
System.out.println(user);
}
}
二、结果对象的创建
本次程序的起点是PreparedStatementHandler的方法,它是查询的结束,同时也是结果封装的开始入口:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
由此,进入到 resultSetHandler.handleResultSets(ps) 方法,而默认会进入到的DefaultResultSetHandler的handleResultSets方法:
@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对象,
// ResultSetWrapper对象含有已映射和未映射的列名和属性的对应关系
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取所有的ResultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// ResultMap的个数
int resultMapCount = resultMaps.size();
// 校验:如果结果集有数据,但是没有定义返回的结果类型,就会报错
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
// 依次获取ResultMap
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);
}
那么,我们重点关注一下这句,因为它才是真正的处理结果集:
// 处理结果集,这里是重点
handleResultSet(rsw, resultMap, multipleResults, null);
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);
// 将处理结果放到list集中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
根据上面的代码,我们关注这一句代码是如何处理的:
// 处理每一行的值
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
查看详细的 handleRowValues 方法:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 如果有嵌套的ResultMap
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
// 处理含有嵌套ResultMap的结果
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 处理不含有嵌套ResultMap的结果
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
由于我们使用了 collection 标签做一对多的映射,所以是属于嵌套的resultMap查询,个人理解,即使是一个实体对象,它也是一个resultMap,只不过它的resultType是实体对象罢了,所以走的嵌套分支:
// 处理含有嵌套ResultMap的结果
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
这个方法比较长:
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;
}
}
我们找重点看吧:
// 看这行代码,获取行值
rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
这其中 getRowValue 方法,我认为是比较重要的方法,处理逻辑大部分都在这里:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
// resultMap的唯一标志id
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())) {
// 将行值包装成元数据对象MetaObject
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
// 是否使用自动映射,因为我们自定义了resultMap,所以不会走这个
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;
}
结果对象的创建,就在这里:
// 创建结果对象
rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
详细如下:
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;
}
你会发现这句,最终也是通过反射创建对象的:
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
三、属性赋值
回到上一步的 DefaultResultSetHandler的getRowValue 方法
通过代码发现,这里的逻辑关系,大概是这样的,sql中的列名 ->通过映射关系,拿到对应的属性名 ->通过属性名,获取set的方法名,最后,通过反射设置值
1.普通属性赋值
// 给对象的属性赋值
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 已经映射的列名集合
final List<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;
} else 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;
}
这里也就是分3步:
(1)根据列名获取值,在resultSet中,根据列名获取值,这个是很容易拿到的
(2)获取属性名,在我们定义的映射规则中就包含了属性名,这个也可以拿到
(3)给对象的属性设置值,这里还是通过反射技术,调用相应的set方法来设值
2.集合对象的属性的设置
回到 DefaultResultSetHandler的getRowValue 方法
// 给嵌套的结果属性赋值
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
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;
}
我们着重看这句:
// 实例化集合属性
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
这一步,便会处理集合属性:
private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
final String propertyName = resultMapping.getProperty();
Object propertyValue = metaObject.getValue(propertyName);
if (propertyValue == null) {
Class<?> type = resultMapping.getJavaType();
if (type == null) {
type = metaObject.getSetterType(propertyName);
}
try {
//如果是集合
if (objectFactory.isCollection(type)) {
// 创建一个空集合
propertyValue = objectFactory.create(type);
// 设置集合属性
metaObject.setValue(propertyName, propertyValue);
// 返回集合属性的引用
return propertyValue;
}
} catch (Exception e) {
throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e);
}
} else if (objectFactory.isCollection(propertyValue.getClass())) {
return propertyValue;
}
return null;
}
那么list集合里面的元素又是在什么时候放进去的呢?这是在DefaultResultSetHandler的applyNestedResultMappings中拿到
行值rowValue后,才进行添加的。
DefaultResultSetHandler的applyNestedResultMappings的部分代码:
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;
}
}
linkObjects方法:
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);
}
}
这样就完成了属性的赋值
控制台的l输出,可以参考一下:
MyBatis 是怎么把查询出来的结果集映射到实体类(JavaBean)上的,原理解释:
相信有很多开发者对MyBatis并不陌生,但谈起MyBatis底层到底是怎么工作的,没个两三年的工程师并非能完全掌握到手,读完以下这篇文章,相信你很快能理解MyBatis是怎么通过JDBC的封装,得到结果集并将结果集映射到JavaBean实体上的一个过程,在这里就不唠叨套多,先来了解下原理。
Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)
MyBatis查询结果集映射到JavaBean原理浅谈
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://lebron.blog.csdn.net/article/details/124585471
内容来源于网络,如有侵权,请联系作者删除!