IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> mybatis-plus查询源码走读 -> 正文阅读

[开发测试]mybatis-plus查询源码走读

配置详情

pom.xml

 <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.4.1</version>
 </dependency>

mapper

public interface GenTableMapper extends BaseMapper<GenTable> {
}

测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = GendemoApplication.class)
public class BlockqueueTestDemo {

    @Autowired
    GenTableMapper genTableMapper;

    @Test
    public void test(){
        List<GenTable> genTables = 
        genTableMapper.selectList(new QueryWrapper<>());
    }
}

debug流程

1.发现 genTableMapper 是一个代理对象类型。

在这里插入图片描述
2.进入代理对象MybatisMapperProxy , 调用其invoke 方法,方法的Class类型为BaseMapper.selectList()

在这里插入图片描述
3.其中cachedInvoker()方法会返回一个PlainMethodInvoker ,它重写了MapperMethodInvoker 接口的invoke()方法

在这里插入图片描述
4.最终会调用MybatisMapperMethodexecute()方法

在这里插入图片描述

public class MybatisMapperMethod {
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
            case INSERT: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
            }
            case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
            }
            case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
            }
            case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) {
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                } else {
                    Object param = method.convertArgsToSqlCommandParam(args);
                    // TODO 这里下面改了
                    if (IPage.class.isAssignableFrom(method.getReturnType())) {
                        result = executeForIPage(sqlSession, args);
                        // TODO 这里上面改了
                    } else {
                        result = sqlSession.selectOne(command.getName(), param);
                        if (method.returnsOptional()
                            && (result == null || !method.getReturnType().equals(result.getClass()))) {
                            result = Optional.ofNullable(result);
                        }
                    }
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
            throw new BindingException("Mapper method '" + command.getName()
                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
    }
}

5.这是经过判断会进入executeForMany(sqlSession, args)方法,此时方法和参数都显示出来了。sqlSession的类型是SqlSessionTemplate , 为什么要注意这个 sqlSession的类型?因为SqlSession是一个接口,有很多实现类,有时候我们并不知道到底调用了哪个实现类的selectList()方法,这个时候我们看类型就知道了,就可以进入SqlSessionTemplate类,找到selectList()打上断点,debug就过来了。

在这里插入图片描述
6.利用同样的方法,又调用了DefaultSqlSessionselectList()方法。

在这里插入图片描述
7.来到DefaultSqlSessionselectList() 方法中,此时已经进入到mybatis的源码范围了。executor的类型是MybatisCachingExecutor

在这里插入图片描述
8.此时要注意MybatisCachingExecutor 代理类的handler是一个Plugin

在这里插入图片描述

在这里插入图片描述
9.因为我使用到了分页插件,所以会来到com.github.pagehelperPageInterceptor

在这里插入图片描述
10.由MybatisCachingExecutor来执行查询

在这里插入图片描述
11.MybatisCachingExecutor 委派 BaseExecutor 执行查询

在这里插入图片描述
在这里插入图片描述
12.最终委派到PreparedStatementHandler来处理

在这里插入图片描述
13.最后由DefaultResultSetHandler来封装结果集

@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);
  }

总结

为什么要一层一层的封装?一层一层的委派?
这里面和缓存有关,有兴趣的可以自己了解一下。

下一篇打算记录一下 mybatis-plus 的自动配置过程,有兴趣的可以持续关注一下。
文中有错误的地方不吝赐教,有好的源码阅读技巧欢迎评论区留言。谢谢。

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:28:06  更:2022-03-06 13:28:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 2:40:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码