本篇对mybatis从取到数据库数据开始到映射成对象并返回的过程进行了详细的分析。

转换ResultSet成java对象

下面的代码是PreparedStatementHandler中的

  @Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;//这里是执行sql并得到ResultSetps.execute();//这里是真正处理ResultSet,将之转换成java对象的地方return resultSetHandler.<E> handleCursorResultSets(ps);}

//resultSetHandler是DefaultResultSetHandler类型的

 @Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());final List<Object> multipleResults = new ArrayList<Object>();int resultSetCount = 0;//把statement中的ResultSet用ResultSetWrapper包装起来(里面有处理类对象的,列名,类名,列名映射等字段)ResultSetWrapper rsw = getFirstResultSet(stmt);//获得mappedStatement中的ResultMaps(这个在初始化configuration的时候就已经生成好了,这里只是取出来)List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();//如果rsw不为空,但是resultMapCount小于1就报错(很容易理解,就是有返回结果,但是没有映射,所以mybatis不知道怎么把结果转换成java类,当然就报错了)validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {//拿到resultMapResultMap resultMap = resultMaps.get(resultSetCount);//处理ResultSet,将处理好的结果存到multipleResults(具体看下面的函数)handleResultSet(rsw, resultMap, multipleResults, null);//从上面这个函数出来的时候,multipleResults已经是存有结果的list了//若stmt还有下一个ResultSet,则继续循环rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}//若你的mapper配置文件中配了这个ResultSets属性的话就取出来往下面走(不过一般都不会用到,最少LZ到现在还没有用过)String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {//不常用就不细细分析了,看完这整篇后,你对mybatis已经有了比较清晰的逻辑了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);}//处理过程private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {//如果有parentMapping则走下面if (parentMapping != null) {//进行处理handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {//若resultHandler为空则走下面if (resultHandler == null) {//初始化一个DefaultResultHandlerDefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);//进行处理handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);//multipleResults中加defaultResultHandler里面的结果集multipleResults.add(defaultResultHandler.getResultList());} else {//进行处理handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)//最后关闭ResultSet回到handleResultSetscloseResultSet(rsw.getResultSet());}}//其实最后走的都是这个函数public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {//看是否有嵌套的ResultMap,(比如使用了association, collection标签)if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();//处理嵌套ResultMaphandleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {//处理简单的ResultMap(以这个为例子)handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}//处理简单的ResultMapprivate void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();//若有分页,则先跳过前面的行数(因为mybatis自带的分页是内存分页,全部数据都取到服务器在进行分页的)skipRows(rsw.getResultSet(), rowBounds);while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {//若要继续处理列并且resultSet还有列则处理//这步拿到要进行处理的resultmapResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);//获得整行的数据,根据map映射成java对象Object rowValue = getRowValue(rsw, discriminatedResultMap);//把结果存好,存在resultHandler中的list中(DefaultResultHandler中有一个字段 List<Object> list用来存储这个结果)storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}}

新建对象并赋值

  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {final ResultLoaderMap lazyLoader = new ResultLoaderMap();//创建一个对象来接受数据(根据配置生成相应的对象)Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final MetaObject metaObject = configuration.newMetaObject(rowValue);boolean foundValues = this.useConstructorMappings;//看是否需要自动映射if (shouldApplyAutomaticMappings(resultMap, false)) {//自动映射,把结果放到metaObject的originalObject也就是rowValue中(具体函数看下方)foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;}//通过属性映射来找foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;foundValues = lazyLoader.size() > 0 || foundValues;//看这段貌似可以在configuration中设置若找不到就实例化一个初始的对象rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;}//返回解析好的行值return rowValue;}

通过mapping映射属性

  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {//这个是自动获取mapping,也是比较重要的函数(下方有具体的分析)List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (autoMapping.size() > 0) {for (UnMappedColumnAutoMapping mapping : autoMapping) {//遍历autoMapping//从resultSet中解析出值//解析的方法很简单,就不跟了,大概就是调用typeHandler中的get函数,取出和列名相对应的值就行了final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {//value不为空说明找到了foundValues = true;}if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {// gcode issue #377, call setter on nulls (value is not 'found')//找到了就把值设置到 metaObject中的objectWrapper中的object中。其实就是metaObject中的originalObjectmetaObject.setValue(mapping.property, value);}}}//返回是否找到return foundValues;}

获取Mapping

//创建自动映射的mappingprivate 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) {//存放映射的UnMappedColumnAutoMapping(一个对应一个字段映射)autoMapping = new ArrayList<UnMappedColumnAutoMapping>();//通过resultSetWrapper来创建未映射的字段的映射(具体方法看下面)final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);for (String columnName : unmappedColumnNames) {//轮询unmappedColumnNamesString propertyName = columnName;if (columnPrefix != null && !columnPrefix.isEmpty()) {// When columnPrefix is specified,// ignore columns without the prefix.if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {propertyName = columnName.substring(columnPrefix.length());} else {continue;}}//通过metaObject找到属性final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());//如果属性不为空且有Set方法的话进入方法if (property != null && metaObject.hasSetter(property)) {//查看该字段set的是什么类型final Class<?> propertyType = metaObject.getSetterType(property);//看typeHandlerRegistry有没有处理这种类型的handle,有的话继续if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {//获取TypeHandlerfinal TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);//生成UnMappedColumnAutoMapping放到autoMapping里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);}}放到cache中autoMappingsCache.put(mapKey, autoMapping);}//返回autoMapping(回到applyAutomaticMappings方法)return autoMapping;}

//ResultSetWrapper类中

  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {//看缓存有没有,没有自己造,有就直接返回List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));if (unMappedColumnNames == null) {//加载映射和没映射字段loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));}return unMappedColumnNames;}private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {List<String> mappedColumnNames = new ArrayList<String>();List<String> unmappedColumnNames = new ArrayList<String>();final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);//获取已经映射过的columnfinal Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);//轮询里面ResultSetWrapper中的columnNamesfor (String columnName : columnNames) {//转成大写比对final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);//若mappedColumns中有此columnif (mappedColumns.contains(upperColumnName)) {//加入到mappedColumnNamesmappedColumnNames.add(upperColumnName);} else {//否则放入unmappedColumnNamesunmappedColumnNames.add(columnName);}}//最后将mappedColumnNames放到mappedColumnNamesMapmappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);//最后将unmappedColumnNames放到unMappedColumnNamesMapunMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);}

总结

其实就几个重要的点。
1.创建对象
2.获得resultHandle
3.获得mapping
4.获得typeHandle
5.通过handle和mapping给对象赋值,然后把对象存到resultHandle中
6.最后返回

Mybatis源码分析之(五)mapper如何将数据库数据转换成java对象的相关推荐

  1. Mybatis源码分析(五)SqlSession的创建

    目录 一 SqlSession的创建 1.1 获取environments配置元素 1.2 获取事务工厂 1.3 获取执行器Executor 1.4 构建DefaultSqlSession 系列文章: ...

  2. MyBatis源码分析(一)MyBatis整体架构分析

    文章目录 系列文章索引 一.为什么要用MyBatis 1.原始JDBC的痛点 2.Hibernate 和 JPA 3.MyBatis的特点 4.MyBatis整体架构 5.MyBatis主要组件及其相 ...

  3. springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)

    springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三) 例: package com.example.demo.service;import com.exa ...

  4. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  5. 源码通透-mybatis源码分析以及整合spring过程

    源码通透-mybatis源码分析以及整合spring过程 mybatis源码分析版本:mybaits3 (3.5.0-SNAPSHOT) mybatis源码下载地址:https://github.co ...

  6. Mybatis源码分析: MapperMethod功能讲解

    canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...

  7. 十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了

    十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花 ...

  8. mybatis源码分析之事务管理器

    2019独角兽企业重金招聘Python工程师标准>>> 上一篇:mybatis源码分析之Configuration 主要分析了构建SqlSessionFactory的过程中配置文件的 ...

  9. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

最新文章

  1. 软件测试人员找工作,去大公司还是去小公司?今天就和大家唠唠
  2. mysql unrecognized_service mysql start出错,mysql启动不了,解决mysql: unrecognized service错误...
  3. PCRE函数简介和使用示例
  4. STL源代码分析(ch2 内存分配)destroy
  5. [WUSTCTF2020]level3
  6. mysql怎么禁止远程连接_mysql如何设置禁止远程连接
  7. allure报告---动态显示模块名和用例标题
  8. 【LeetCode】盛最多水的容器【双指针+贪心 寻找最大面积】
  9. ASP.NET C#读写Cookie的方法!
  10. 自己不能跑的车凭什么叫自行车?B站硬核up主把自行车做成了自动驾驶
  11. 很无聊但是又很重要的 计算机网络基础知识 ---“互联网协议“
  12. ICON 文件构成 及 制作工具
  13. 让微信保持高度活跃的利器
  14. NODA-GA-NHS ester CAS:1407166-70-4 大环配体配合物
  15. 纯代码蓝色理想的logo
  16. LINUX 下无线网卡 rtl8821CE/rtl8723de 驱动 无法驱动解决办法
  17. 学习编程太枯燥?12款助你学编程的免费游戏送上!
  18. 极限学习机的使用方法
  19. 计算机控制闪光灯,并联控制式自动调光闪光灯 - 最全的照相机闪光灯电路图大全(十款照相机闪光灯电路图详解)...
  20. 做了6年的Java,mysql去重查询方法

热门文章

  1. debug error怎么解决_我要以血和泪的经历告诉你,这个 bug 太难解决了
  2. 总结计算机语言的基本元素,认识程序设计中基本元素教案.doc
  3. springboot项目后台运行关闭_springboot项目在服务器上部署过程(新手教程)
  4. 3dmax镜像后模型线条乱了_3dMax入门教程来啦!小白赶紧收藏!
  5. ubuntu mysql开发_ubuntu linux mysql 开发模式与连接编译
  6. python 编程快速上手,Python编程快速上手
  7. 线程池是如何执行的?拒绝策略有哪些?
  8. SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!
  9. iBatis for net 框架使用
  10. 【C++】For循环同时初始化两个变量