mybatis原理:结果集封装详解
经过sql参数解析、sql动态组装和执⾏sql,相对⽽⾔,结果集的封装,是mybatis数据处理的最后⼀环。这⾥只对查询结果⽽⾔,因为更新语句⼀般都是返回影响的⾏数。抛开mybatis,如果让我们组装结果,我们该如何进⾏呢?mybatis的查询结果统⼀表⽰为:
水果美容List<E>
即使是查询单个对象,它的查询结果还是封装成 List<E> 对象,然后返回list集合的第⼀个元素。
个⼈根据mybatis的源码,将mybatis对结果集的封装,分成两步:
(1)通过反射,创建结果对象,其所有属性为默认值,例如,如果结果是实体对象,那么将通过⽆参构造函数创建对象,其所有属性⼀般为空,如果结果是List,则会创建⼀个空的List
(2)为结果对象的属性赋值,这⾥也是通过反射,到set⽅法赋值
下⾯开始进⼊主题:
⼀、数据准备
1. 查询sql以及⾃定义映射的resultMap:
<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>
ie主页被修改从查询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 = ResourceAsStream("l");
//根据配置信息,创建SqlSession⼯⼚
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/
/SqlSession⼯⼚创建SqlSession
SqlSession sqlSession = factory.openSession();
//获取接⼝的代理对象
UserMapper mapper = Mapper(UserMapper.class);
User user = UserAndBlogByUserId("123");
System.out.println(user);
}为什么wifi连接上却不能上网
}
⼆、结果对象的创建
本次程序的起点是PreparedStatementHandler的⽅法,它是查询的结束,同时也是结果封装的开始⼊⼝:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
return resultSetHandler.handleResultSets(ps);
}
由此,进⼊到 resultSetHandler.handleResultSets(ps) ⽅法,⽽默认会进⼊到的DefaultResultSetHandler的handleResultSets ⽅法:
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").Id()); // 要返回的结果
final List<Object> multipleResults = new ArrayList<>();
// 迭代变量,结果集的个数
吨的拼音int resultSetCount = 0;
// 获取第⼀个结果集,并包装成ResultSetWrapper对象,
// ResultSetWrapper对象含有已映射和未映射的列名和属性的对应关系
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取所有的ResultMap
List<ResultMap> resultMaps = ResultMaps();
// ResultMap的个数
int resultMapCount = resultMaps.size();
// 校验:如果结果集有数据,但是没有定义返回的结果类型,就会报错
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
// 依次获取ResultMap
ResultMap resultMap = (resultSetCount);
// 处理结果集,这⾥是重点
handleResultSet(rsw, resultMap, multipleResults, null);
// 获取下⼀个结果集
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = ResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = (resultSets[resultSetCount]); if (parentMapping != null) {
String nestedResultMapId = NestedResultMapId();
ResultMap resultMap = ResultMap(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 SQLExce 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.ResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
ResultSet());
}
}
根据上⾯的代码,我们关注这⼀句代码是如何处理的:
// 处理每⼀⾏的值
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
查看详细的 handleRowValues ⽅法:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping pa // 如果有嵌套的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 rowBoun final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = ResultSet();
// 跳过已处理的⾏
skipRows(resultSet, rowBounds);
Object rowValue = previousRowValue;
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && ()) {
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
Object partialObject = (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 ⽅法,我认为是⽐较重要的⽅法,处理逻辑⼤部分都在这⾥:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论