浅析Mybatis的ORM的执行流程及原理

在这里插入图片描述

前言

返璞归真,今天看下mybatis的执行流程。

先来案例

一、首先是配置文件,需要一个xml的配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <typeAliases>  <!--配置实体类的别名-->
        <package name="fast.cloud.nacos.mybatis.entity"/>
    </typeAliases>

    <environments default="MySQLDevelopment">
        <environment id="MySQLDevelopment">
            <transactionManager type="JDBC"></transactionManager> <!--事务-->
            <dataSource type="POOLED">  <!--数据库连接-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="fast.cloud.nacos.mybatis.mapper"/>
    </mappers>

</configuration>

二、定义Mapperentity,详细代码在https://github.com/fafeidou/fast-cloud-nacos/tree/master/fast-source-code-analysis/code-mybatis
在这里插入图片描述

三、查询,如下所示

@Test
    public void testMybatis() throws IOException {
        //1. 加载核心配置文件
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");

        //2. 解析核心配置文件并创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        //3. 创建核心对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //4. 得到Mapper代理对象
        DeptEmpMapper deptEmpMapper = sqlSession.getMapper(DeptEmpMapper.class);

        //5. 调用自定义的方法实现查询功能
        List<DeptEmp> list = deptEmpMapper.getEmpTotalByDept();
        for (DeptEmp de : list) {
            System.out.println(de);
        }

        //6. 关闭sqlSession
        sqlSession.close();

    }

逐步分析

我想着是以打断点的方式,逐步去看mybatisorm

  1. 解析MyBatis核心配置文件并创建SqlSessionFactory 对象
//1. 加载核心配置文件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");

//2. 解析核心配置文件并创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

这三行代码先是创建了SqlSessionFactoryBuilder 对象,然后获得了MyBatis 核心配置文件的输入流,最后调用了build 方法,我们接下来跟踪一下该方法的源码。
在这里插入图片描述
第77 行代码创建了一个xml 解析器对象,并在第78 行对MyBatis 核心配置文件进行了解析,拿到了数据库连接数据以及映射配置文件中的数据(包括我们编写的sql 语句和自定义的resultMap),XMLConfigBuilderparse方法,将xml的配置解析成java对象。

在这里插入图片描述
XMLConfigBuilder#environmentsElement设置数据源和事务管理器
在这里插入图片描述
观察configuration对象,这个时候可看到已经初始化好数据源和扫描到的mapper
在这里插入图片描述
还有mapper里面的方法
在这里插入图片描述
还有resultMap映射
在这里插入图片描述
在DefaultSqlSessionFactory 的构造方法中,我们发现,解析后拿到的数据最终被全部存入到了一个名字为Configuration 的对象中,后续很多操作都会从该对象中获取数据。

SqlSession sqlSession = sqlSessionFactory.openSession();

在这里插入图片描述
图中红框部分代码,主要通过读取Configuration 对象中的数据分别创建了Environment 对象,事务对象,Executor 对象等,并最终直接new 了一个DefaultSqlSession 对象(SqlSession 接口的实现类),该对象是MyBatis API 的核心对象。

DeptEmpMapper deptEmpMapper = sqlSession.getMapper(DeptEmpMapper.class);

在这里插入图片描述
最终调用了MapperRegistry 对象的getMapper 方法,把实体类和sqlSession对象传了过去。

在这里插入图片描述
第50 行代码通过代理工厂最终把我们自定义的Mapper 接口的代理对象创建了出来。

List<DeptEmp> list = deptEmpMapper.getEmpTotalByDept();

在这里插入图片描述

当我们调用代理对象的getEmpTotalByDept()方法时,框架内部会调用MapperProxy 的invoke方法, 我们可以观察一下这个方法三个参数的值,如下图所示

在这里插入图片描述

在invoke 方法内锁定第53行代码,该行代码调用MapperMethod 的execute 方法实现查询功能。

在这里插入图片描述

在execute 方法内先进行增删改查判断,本案例进行的是查询,并且可能会查询出多条记录,所以最后锁定第68 行代码,该行代码调executeForMany 方法进行查询。

在这里插入图片描述

第124 行代码通过hasRowBounds()方法判断是否进行分页查询,本案例不需要,所以执行else 中的代码,调用sqlSession 的selectList 方法(第128 行), 参数的值如下图所示:
在这里插入图片描述

第147 行代码从Configuration 对象中获得MappedStatement 对象,该对象存储了映射配置文件中的所有信息(包括我们编写的sql 语句和自定义的resultMap),如下图所示:

在这里插入图片描述

第124 行代码调用了CachingExecutor 的query(…)方法, 继续往下跟踪。

在这里插入图片描述
第81 行通过ms 对象从mapper 映射配置文件中获得了sql 语句,如下图所示,第93 行代码调用了下面的query 方法,该方法内部先从缓存中取数据,如果缓存中没数据,就重新查询(第87 行),继续往下跟踪。
在这里插入图片描述
在这里插入图片描述
缓存没有数据就会查询BaseExecutor#query的queryFromDatabase这个方法,就是从数据库中获取数据。
在这里插入图片描述
最终执行了sql 语句并将查询结果按照我们自定义的resultMap 进行了封装,结果如下图所示
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值