• 一次Mybaits查询的源码分析


    很好奇Mybaits是怎么将xml和mapper对应起来的,用一段比较简单的demo去debug追踪一下源码看看

    先用xml配置的方式,看懂了再去看注解的方式是怎么实现的

    获取Mapper

    Mybaits是如何从xml中加载到mapper的

    
    configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <package name="com.github.yeecode.mybatisdemo"/>
        typeAliases>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                    <dataSource type="POOLED">
                        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC"/>
                        <property name="username" value="root"/>
                        <property name="password" value="123456"/>
                    dataSource>
            environment>
        environments>
        <mappers>
            <mapper resource="com/github/yeecode/mybatisdemo/UserMapper.xml"/>
        mappers>
    configuration>
    

    在xml中有mapper标签,应该是从这里加载到配置

    示例代码

      public static void main(String[] args) {
            // 第一阶段:MyBatis的初始化阶段
            String resource = "mybatis-config.xml";
            // 得到配置文件的输入流
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 得到SqlSessionFactory
            SqlSessionFactory sqlSessionFactory =
                    new SqlSessionFactoryBuilder().build(inputStream);
    
            // 第二阶段:数据读写阶段
            try (SqlSession session = sqlSessionFactory.openSession()) {
                // 找到接口对应的实现
                UserMapper userMapper = session.getMapper(UserMapper.class);
                // 组建查询参数
                User userParam = new User();
                userParam.setSchoolName("Sunny School");
                // 调用接口展开数据库操作
                List userList =  userMapper.queryUserBySchoolName(userParam);
                // 打印查询结果
                for (User user : userList) {
                    System.out.println("name : " + user.getName() + " ;  email : " + user.getEmail());
                }
            }
        }
    

    UserMapper userMapper = session.getMapper(UserMapper.class);此处开始debug,看看是怎么获取到mapper的

    一路点进来发现是从一个Map中去获取mapper对象

    private final Map, MapperProxyFactory> knownMappers = new HashMap<>();

    那么是从什么时候填充knownMappers的呢

    这个对象的方法不只有获取mapper,还有添加mapper,找一个添加方法继续断底点

    找到是找到了,但是不知道什么时候调用的,可以通过IDEA的调用栈

    最后发现了,是加载xml的时候,去解析mapper标签里的值,然后再通过类加载器去加载资源,最后加载到knownMappers中,还有去解析xml中的sql的过程

    这样子xml就和mapper就对应起来了,虽然知道了mapper和xml的对应关系,但是不知道怎么通过调用mapper里的方法,去找到对应的sql

    在对 List userList = userMapper.queryUserBySchoolName(userParam);debug时,没有进入到mapper方法中,而是会进入到一个代理类中

    刚刚在getMapper()中给UserMapper创建了代理,那么大概知道是mapper和xml是怎么关联的了,当调用mapper时,会被MapperProxy代理,去执行查询方法时,通过上边的knownMappers
    获取到mapper对应的xml,这样代理类就知道要调用的方法和对应的sql的哪里

    最终时通过mapperMethod.execute(sqlSession, args);去执行查询的,点进去一看发现对各种sql类型做了处理

    select的查询原来通过返回值来选择不同的处理

    很好奇这些属性是怎么判断的,找到对应的类继续断点

    原来是在execute之前去赋值,而且这个方法会把调用方法对应的xml中的方法找到

    通过获取到方法的返回值,然后再去做对比,我这个方法返回的是list,就命中了returnMany,在继续断点,找到了真正执行的方法

    这里就已经将关联的xml信息带过来了


    继续看会看到缓存相关的代码,如果命中了缓存就直接返回了,我这里没有就继续开了一个线程往下执行,delegate是一个Executor

    最后的查询到了这里,就是调用mysql的包了,在statement中,已经把sql、参数和连接配置什么的都封装好了

    查询完后把结果返回到statement,但是返回的内容很多,查询结果记录在这里

    查询总结:

    • 在进行数据库查询前,先查询缓存;没有名中,则数据库查询之后的结果也放入缓存中
    • SQL 语句的执行经过了层层转化,依次经过了 MappedStatement 对象、Statement对象和 PreparedStatement对象,最后才交给mysql执行
    • 最终数据库查询得到的结果交给 ResultHandler对象处理

    返回结果

    将结果映射到实体类上这段代码有点绕,调用链很长

    首先是这里先创建输出的实体类,就是resultMap里定义的对象

    创建好实体后,把实体传输给下一个方法,填充实体

    将实体字段和结果集里的字段对应起来,然后根据字段去获取对应的值,然后把值设置到实体里,通过循环遍历全部字段

    这样走一圈回来,一个对象就映射好了,再经过循环,就把全部的对象都拿到了,最后再将这些对象封装到multipleResults集合里,这个集合就是返回值了

    映射总结:

    • 获取并创建实体类
    • 将实体类的字段和结果集的字段一一对应,然后再填充实体的值
    • 最后返回实体集合

    总结

    以上就是Mybaits读取xml,然后查询的过程了,整个过程还是很复杂的,很多层封装和跳转,但是大大的提高了我们开发的效率

    然后再把总结发一下

    获取配置总结:

    • 得到配置文件然后转换成输入流
    • 将输入流传给SqlSessionFactoryBuilder创建SqlSessionFactory
    • 扫描xml文件并加载,然后将xml和mapper的对应关系填充好

    查询总结:

    • 在进行数据库查询前,先查询缓存;没有名中,则数据库查询之后的结果也放入缓存中
    • SQL 语句的执行经过了层层转化,依次经过了 MappedStatement 对象、Statement对象和 PreparedStatement对象,最后才交给mysql执行
    • 最终数据库查询得到的结果交给 ResultHandler对象处理

    映射总结:

    • 获取并创建实体类
    • 将实体类的字段和结果集的字段一一对应,然后再填充实体的值
    • 最后返回实体集合
  • 相关阅读:
    深入理解Mysql索引
    【2022秋线上作业-第5次-第11-13周】判断题
    最长连续序列(LeetCode128)
    Python编写告警信息,整合Alertmanager告警
    计算机毕业设计ssm+vue基本微信小程序的体检预约小程序
    Python 全栈系列202 使用Mongo和Redis提供简单查询的服务
    MySQL索引、事务与存储引擎
    QT软件开发-基于FFMPEG设计视频播放器-支持软解与硬解(二)
    TiDB Lightning 并行导入
    java计算机毕业设计疫情期间高校师生外出请假管理系统录屏源程序+mysql+系统+lw文档+远程调试
  • 原文地址:https://www.cnblogs.com/aruo/p/17486845.html