Mybatis(六) - Mybatis是如何将结果封装成Java bean的

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 defaultResultHandlerlist 个数就会增加到查询处正确结果的个数,请看图:
    在这里插入图片描述
  • 1.1.10 当执行完 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); 后请看图:
    在这里插入图片描述
    结果就是4条,成功将数据返回,并且成功封装成了 Java Bean 对象
二、步骤分析
  • 其实这也不是特别的难以理解,就是将结果处理完毕后,放入到上下文的返回结果中,然后当所有的数据都从 Statement 中取完毕的时候,直接从上下文中取出结果即可。
三、总结
  • 从最初的例子,到封装成 Java Bean 拿到查询结果,我用了 六 篇博客来简单描述,其中包含了各种对象的初始化操作,参数的封装操作,sql 与 statementId 映射,sql 返回结果的映射,结果集的处理,得到 Java Bean,一系列繁琐的操作,都是 Mybatis 帮我们完成的。回想起 JDBC 的那段时间,是苦不堪言的。阅读大佬的源码的同时,也需要做更多的笔记来加深对知识的消化,来提升自我。
  • 以上所有内容(除源码外) 是由我手写的,如有错别字或者错误的地方请大佬们指正,相互学习才是成长的阶梯。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis-plus 提供了分页插件 PageHelper,可以方便地进行分页查询。使用方式如下: 1. 引入依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.x.x</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.x</version> </dependency> ``` 2. 配置分页插件: ```java @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } } ``` 3. 编写查询方法: ```java @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override public IPage<User> getUserPage(int pageNum, int pageSize) { Page<User> page = new Page<>(pageNum, pageSize); return baseMapper.selectPage(page, null); } } ``` 其中,pageNum 表示当前页码,pageSize 表示每页大小。使用 Page 对象创建一个分页查询对象,然后调用 selectPage 方法进行查询。查询结果会封装在 IPage 对象中,包含了当前页数据以及分页信息。 4. 在 Controller 中调用查询方法: ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/list") public IPage<User> getUserList(@RequestParam("pageNum") int pageNum, @RequestParam("pageSize") int pageSize) { return userService.getUserPage(pageNum, pageSize); } } ``` 在 Controller 中接收 pageNum 和 pageSize 参数,然后调用 Service 中的 getUserPage 方法进行查询,返回查询结果。 以上就是使用 Mybatis-plus 进行分页查询的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值