手写框架系列----Mybatis-----基本和源码长得差不多。写完再用真正的Mybatis改造,讲解Mybatis高级应用和源码剖析

源码(码云):https://gitee.com/yin_zhipeng/implement_mybatis_of_myself.git
测试使用的数据库如下(sql文件已在源码中提供)

在这里插入图片描述

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (
  `id` char(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `money` int(20) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'A', 100);
INSERT INTO `account` VALUES ('2', 'B', 100);

SET FOREIGN_KEY_CHECKS = 1;

一、手写Mybatis

1. 概念和基本分析

Mybatis是持久层框架的一种,对原生JDBC进行封装,并实现sql与逻辑的分离
  1. JDBC的问题和解决思路
    在这里插入图片描述
  2. 自定义Mybatis的思路
    在这里插入图片描述
    在这里插入图片描述
关键在于如何合理规定用户提供的xml配置文件,有了配置文件就好说了,直接解析,然后反射,拿到反射的东西,再使用JDBC完成操作
  1. sqlMapConfig.xml:存放数据库配置信息,和mapper.xml的全路径
  1. 这个文件随意一点就好,因为是基本配置完成一次,就不需要动了
  2. 提供数据库配置信息和mapper.xml全路径就好
  1. mapper.xml
  1. 这个文件就需要深思熟虑了,我们必须关心,那么多mapper.xml如何保证每一条sql语句不冲突,当执行动态sql时,如何封装用户传过来的条件参数。如何规定结果集封装的类型。
  2. 关于保证每一条sql不冲突的解决办法:
  1. 我们希望,每个mapper.xml文件的配置具有统一性,比如查询所有数据,我们希望都叫做< select id=“selectList”>.那么名字都一样如何准确定位这些sql呢?
  2. 所以我们决定在根标签定义唯一标识,它是整个mapper文件的id。起名为namespace
  1. 关于结果集封装的解决方案:我们要通过反射等技术,进行封装,就必须知道要反射什么类。所以需要结果集的sql,只需要指定resultType=“全限定路径”。我们就可以完成反射
  2. 关于使用用户传来的条件参数,我们依然通过全限定路径,然后根据属性名进行封装。使用形式就是#{属性名}
  1. 例子如下
<!--我们规定Mapper配置文件,根标签用mapper,并且需要提供一个此配置文件的唯一标识,namespace。
    有了namespace,我们mapper标签中配置的内容就可以重复,而不用每个Mapper配置文件,都得起不一样的名字-->
<mapper namespace="bank">
    <!--通过select标签封装查询sql语句,同样每一条sql,都需要有唯一标识,我们就通过namespace.id的形式标识吧。
        - 最终我们解析的时候就统一用statementId来封装
        - 同样的我们也得标识,最终查询出来的结果集如何封装,通过resultType指定-->
    <select id="selectList" resultType="com.yzpnb.entity.Account">
        select * from account
    </select>
    <!--而对于动态参数,我们也需要封装,通过paramterType指定。并且我们人为规定,用#{属性名}的形式来使用
        select * from account where id = ? ==转变为==> select * from account where id = #{id}-->
    <select id="selectOne" resultType="com.yzpnb.entity.Account" paramterType="com.yzpnb.entity.Account">
        select * from account where id = #{id}
    </select>
</mapper>

2. 使用端提供xml

使用端,就是平常使用mybatis框架的我们。主要负责引入mybatis的jar包,然后提供相关配置文件,然后使用即可,我们单独写一个模块作为使用端

在这里插入图片描述

1. 提供xml:这些xml的标签没有任何规定,只是我们解析时按照这个规则解析。而且毕竟是手写mybatis,当然最好规则和mybatis一样喽。所以和mybatis配置文件基本上一模一样
  1. 核心配置,指定数据库连接和mapper.xml文件全路径的配置
    在这里插入图片描述
  2. mapper.xml(只提供了select的,因为已经把很多种情况都涵盖了,update,delete等大同小异)
    在这里插入图片描述
2. 其它操作就是引入自己实现的mybatis,使用提供的功能。但是现在还没写,所以接下来先写自定义MyBatis

3. 自定义MyBatis,并不断通过使用端测试

新建模块,实现自定义MyBatis,然后使用端引入
  1. 自定义MyBatis模块
    在这里插入图片描述
  2. 使用端引入
    在这里插入图片描述
为了节省篇幅,我直接将所有需要用到的jar,全部通过maven导入

在这里插入图片描述

3.1 加载配置文件

1. 自实现Mybatis提供相关方法

在这里插入图片描述

2. 使用端,使用,测试

在这里插入图片描述

3.2 创建两个javaBean容器对象

两个容器对象,分别存储sql配置映射关系(MappedStatement),和核心配置(Configuration)
  1. 另外,MappedStatement是一个容器对象,相当于实体类,不是数组集合,而是单独存放一个sql映射的容器,一个sql映射对应一个MappedStatement对象
1. MappedStatement,映射配置类

在这里插入图片描述

2. Configuration,核心配置类。存储数据源和保存每一个MappedStatement对象

在这里插入图片描述

3.3 使用dom4j解析配置文件,并封装,然后构建会话对象

提供SqlSessionFactoryBuilder类并通过build(InputStram in)方法。通过前面加载核心配置文件的输入流,使用dom4j解析配置文件,然后进行封装。最后创建SqlSessionFactory对象,生产sqlSession会话对象
  1. SqlSessionFactory是一个接口,一个工厂设计模式,用来生产sqlSession会话对象,遵守开闭原则
1.我们要将从mapper.xml解析的东西封装到MappedStatement,然后将MappedStatement再封装到Configuration对象中,所以通过构建者模式,单独用一个类来构建

在这里插入图片描述

    /**jaxen表达式,用来匹配xml结点
     * 常用表达式
     * nodename 选取此节点的所有子节点
     * /        从根结点选取
     * //       从当前匹配的节点选取,会选取当前节点下的所有匹配的子节点(不考虑层级,包括孙子节点)
     * *        选取所有
     * **       选取当前结点的父节点
     * @        选取属性
     */
2. 我们要将最后从xml解析的东西都封装到Configuration对象中,所以通过构建者模式,单独用一个类来构建

在这里插入图片描述
在这里插入图片描述

3. 创建SqlSessionFactory接口,工厂设计模式,专门用来生产SqlSession对象,当然现在我们还不需要实现,先将规则放在那即可

在这里插入图片描述

4. DefaultSqlSessionFactory类实现SqlSessionFactory接口,生产sqlSession对象,SqlSession对象需要使用我们Configuration对象,所以将其封装进去,而Configuration对象需要进行构建,所以整个SqlSessionFactory都需要构建

在这里插入图片描述

5. 创建SqlSessionFactoryBuilder类,构建者模式,构建SqlSessionFactory

在这里插入图片描述

3.4 编写SqlSessionFactory接口和实现类,生产sqlSession

上面我们构建好了SqlSession对象需要的Configuration对象,并将其交给了SqlSession。接下来要生产sqlSession
1. 创建SqlSession对象,也是一个接口,和需要的实现类
  1. SqlSession接口
    在这里插入图片描述
  2. 实现类
    在这里插入图片描述
2. SqlSessionFactory接口定义规范

在这里插入图片描述

2. DefaultSqlSessionFactory实现类,实现接口

在这里插入图片描述

3.5 编写SqlSession接口和实现类,定义对数据库crud操作

有了SqlSession对象,我将就可以通过SqlSession操作了,我们只模拟查询的操作,其它操作大同小异
遵循开闭原则,JDBC和SqlSession不可以耦合,所以我们继续抽出一层,这样实现的好处是,使用端,可以统一的封装,也可以自己单个实现,并且完全不耦合
  1. Executor接口,它是更抽象的一层,所以名字叫query就好
    在这里插入图片描述
  2. 我们可以自己提供一个实现,当然一般用户都自己去实现
    在这里插入图片描述
可见query需要Configuration对象,所以我们在生产SqlSession时,需要给它传过去

在这里插入图片描述

SqlSession接口

在这里插入图片描述

实现类

在这里插入图片描述

3.6 编写Executor接口及实现类,执行JDBC代码

执行JDBC代码首先需要SQL语句,和动态参数,例如select * from account where id = ?。但是现在我们只有select * from account where id = #{id}。所以我们将其转换,包括#{id},像id这样的参数也有取出来,我们可以单独封装成一个对象BoundSql
  1. 我们需要借助一些工具类(我从mybatis源码中直接拿过来用)
  1. 参数名称封装类,ParameterMapping
    在这里插入图片描述
  2. TokenHandler接口
    在这里插入图片描述
  3. ParameterMappingTokenHandler实现TokenHandler接口,负责解析sql
    在这里插入图片描述
  4. 通用Token解析器,GenericTokenParser,通过开始标记,结束标记解析,干活的是上面TokenHandler接口的实现类
    在这里插入图片描述
  1. BoundSql实体类
    在这里插入图片描述
接下来正式编写query,就是基本的通过反射拿到参数和返回类型,然后编写JDBC代码,还是很简单的。反射和JDBC都是java的基础,都是非常简单的概念,大家自己看注释吧。
  1. 提供getBoundSql方法,解析sql,封装BoundSql
    在这里插入图片描述
  2. 提供getClassType方法,根据全限定类名,返回class对象
    在这里插入图片描述
  3. 反射和执行JDBC的代码==>注册驱动,获取连接==>sql语句转换==>获取预处理对象 JDBC的preparedStatement==>如果有占位符,就设置参数==>执行sql
    在这里插入图片描述
  4. 结果集封装
    在这里插入图片描述

4. 使用端测试,功能优化

我们来试一下效果

在这里插入图片描述

4.1 问题分析

虽然功能实现了,但是想一想,我们平常是在持久层(dao,mapper)编写相关代码,显然上面内种方式是不好的,每个方法,都需要写重复的代码,一般我们只是写一个接口,而不需要实现类
  1. 持久层接口
    在这里插入图片描述
我们希望,只是定义接口,其它什么都不需要关心。很明显,又要用到代理设计模式

4.2 动态代理改造,持久层只需接口,无需实现类

那么我们什么都不写,怎么知道调用哪个sql,我们现在只知道是哪个持久层接口,接口中哪个方法。所以既然只知道持久层接口,那我们用接口名字当namespace不就好了。而< select id="">,id这个标识,就和接口中方法对应就好了

在这里插入图片描述

1. SqlSession提供方法,为持久层接口生成代理实现类

在这里插入图片描述
2. 实现类实现
在这里插入图片描述

测试

在这里插入图片描述

二、Mybatis高级应用

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/122927196

三、Mybatis源码

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/122927311
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殷丿grd_志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值