自己实现MyBatis 底层机制--抽丝剥茧(上)

😀前言
本篇博文是学习过程中的笔记和对于MyBatis底层机制的分析思路,希望能够给您带来帮助😊

🏠个人主页:晨犀主页
🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉

💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看🥰
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦😊

文章目录

  • 自己实现MyBatis 底层机制[上]
    • MyBatis 整体架构分析
      • Mybatis 核心框架示意图
      • 核心框架示意图的解读
    • 搭建MyBatis 底层机制开发环境
      • 1、创建Maven 项目nlc-mybatis
      • 2、修改nlc-mybatis\pom.xml
      • 3、创建数据库和表
      • 4、到此: 项目开发环境搭建完成
    • Nlc-Mybatis 的设计思路
      • Mybatis 的底层实现设计
        • 1. 传统方式操作数据库
        • 2.MyBatis操作数据库的方式分析
    • 自己实现MyBatis 底层机制
      • 实现任务阶段1- 完成读取配置文件,得到数据库连接
        • 分析示意图
        • 代码实现
        • 完成测试
        • 测试效果
    • 实现任务阶段2- 编写执行器,输入SQL 语句,完成操作
      • 分析示意图
      • 代码实现
      • 完成测试
    • 😄总结

自己实现MyBatis 底层机制[上]

MyBatis 整体架构分析

Mybatis 核心框架示意图

核心框架示意图的解读

  1. mybatis 的核心配置文件
    mybatis-config.xml: 进行全局配置,全局只能有一个这样的配置文件
    XxxMapper.xml 配置多个SQL,可以有多个XxxMappe.xml 配置文件
  2. 通过mybatis-config.xml 配置文件得到SqlSessionFactory
  3. 通过SqlSessionFactory 得到SqlSession,用SqlSession 就可以操作数据了
  4. SqlSession 底层是Executor(执行器), 有两个重要的实现类基本执行器和带缓存功能的执行器, 有很多方法

  1. MappedStatement 是通过XxxMapper.xml 中定义, 生成的statement 对象
  2. 参数输入执行并输出结果集, 无需手动判断参数类型和参数下标位置, 且自动将结果集映射为Java 对象

搭建MyBatis 底层机制开发环境

1、创建Maven 项目nlc-mybatis

前面快速入门有创建步骤,这里不在描述。

2、修改nlc-mybatis\pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.nlc</groupId>
    <artifactId>nlc-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--定义编译器 / source / target 版本即可-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>
    </properties>
    <!--引入必要的依赖-->
    <dependencies>
        <!--引入dom4j-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--引入mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!--lombok-简化entity/javabean/pojo开发 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

3、创建数据库和表

CREATE DATABASE `nlc_mybatis`;
USE `nlc_mybatis`;
CREATE TABLE `monster` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `age` INT NOT NULL,
    `birthday` DATE DEFAULT NULL,
    `email` VARCHAR(255) NOT NULL,
    `gender` TINYINT NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `salary` DOUBLE NOT NULL,
    PRIMARY KEY (`id`)
) CHARSET=utf8
INSERT INTO `monster` VALUES(NULL, 200, '2000-11-11', 'nmw@sohu.com', 1,'牛魔王', 8888.88)

4、到此: 项目开发环境搭建完成

Nlc-Mybatis 的设计思路

Mybatis 的底层实现设计

1. 传统方式操作数据库

  1. 得到Session对象

  2. 调用Executor的方法完成操作

  3. Executor的连接是从Configuration获取的

2.MyBatis操作数据库的方式分析

  1. 得到Session
  2. 不用直接调用Executor的方法完成操作
  3. 通过MapperProxy获取Mapper对象
  4. 调用Mapper的方法,完成数据库的操作
  5. Mapper最终还是动态代理方式,使用Executor的方法完成操作
  6. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

自己实现MyBatis 底层机制

实现任务阶段1- 完成读取配置文件,得到数据库连接

通过配置文件,获取数据库连接。

分析示意图

代码实现

  1. 创建nlc-mybatis\src\main\resources\nlc_config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<database>
    <!--配置连接数据库的信息-->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <!--配置连接mysql-url
                1. jdbc:mysql 协议
                2. 127.0.0.1:3306 : 指定连接mysql的ip+port
                3. mybatis: 连接的DB
                4. useSSL=true 表示使用安全连接
                5. &amp; 表示 & 防止解析错误
                6. useUnicode=true : 使用unicode 作用是防止编码错误
                7. characterEncoding=UTF-8 指定使用utf-8, 防止中文乱码
                8. 不要背,直接使用即可
        -->
    <property name="url" value="jdbc:mysql://localhost:3306/nlc_mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</database>
  1. 创建nlc-mybatis\sqlsession\NlcConfiguration.java
public class NlcConfiguration {

    //属性-类的加载器
    private static ClassLoader loader =
            ClassLoader.getSystemClassLoader();

    //读取xml文件信息,并处理
    public Connection build(String resource) {

        Connection connection = null;

        try {
            //加载配置nlc_mybatis.xml 获取到对应的InputStream
            InputStream stream =  loader.getResourceAsStream(resource);
            SAXReader reader = new SAXReader();
            Document document = reader.read(stream);
            //获取到nlc_mybatis.xml 的根元素 <database>
            Element root = document.getRootElement();
            System.out.println("root=" + root);
            //解析root元素,返回Connection 
            connection = evalDataSource(root);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    //方法会解析nlc_config.xml 信息,并返回Connection
    //eval: 评估/解析
    private Connection evalDataSource(Element node) {
        if (!"database".equals(node.getName())) {
            throw new RuntimeException("root 节点应该是<database>");
        }
        //连接DB的必要参数
        String driverClassName = null;
        String url = null;
        String username = null;
        String password = null;

        //遍历node下的子节点,获取属性值
        for (Object item : node.elements("property")) {
            Element i = (Element) item;//i 就是 对应property节点
            String name = i.attributeValue("name");
            String value = i.attributeValue("value");

            //判断是否得到name 和 value
            if (name == null || value == null) {
                throw new RuntimeException("property 节点没有设置name或者value属性");
            }
            switch (name) {
                case "url":
                    url = value;
                    break;
                case "username":
                    username = value;
                    break;
                case "driverClassName":
                    driverClassName = value;
                    break;
                case "password":
                    password = value;
                    break;
                default:
                    throw new RuntimeException("属性名没有匹配到...");
            }
        }

        Connection connection = null;

        try {
            //要求JVM查找并加载指定的类到内存中,此时将"com.mysql.jdbc.Driver" 当做参数传入,
            // 就是告诉JVM,去"com.mysql.jdbc"这个路径下找Driver类,将其加载到内存中。
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url,username,password);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connection; //返回Connection
    }

}

完成测试

  1. 编写nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java
public class NlcMyBatisTest {

    @Test
    public void build() {
        NlcConfiguration nlcConfiguration = new NlcConfiguration();
        Connection connection = nlcConfiguration.build("nlc_mybatis.xml");
        System.out.println("connection--" + connection);
    }
}

测试效果

实现任务阶段2- 编写执行器,输入SQL 语句,完成操作

说明:通过实现执行器机制,对数据表操作。

分析示意图

说明:我们把对数据库的操作,会封装到一套Executor 机制中,程序具有更好的扩展性,结构更加清晰.

代码实现

  1. 创建nlc-mybatis\src\main\java\com\nlc\entity\Monster.java

如果使用@Data注解需要全参构造器可以添加@AllArgsConstructor,但是无参构造器必须要显示调用,否则会被全参构造器覆盖。

/**
 * Monster 和 monster表有映射关系
 * @Getter 就会给所有属性 生成对应的getter
 * @Setter 就会给所有属性 生成对应的setter
 * @ToString 生成 toString...
 * @NoArgsConstructor 生成无参构造器
 * @AllArgsConstructor 生成要给全参构造器
 * @Data 会生成上面除全参构造器的所有注解
 * 如何选择主要还是看自己需要
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Monster {

    private Integer id;
    private Integer age;
    private String name;
    private String email;
    private Date birthday;
    private double salary;
    private Integer gender;

}
  1. 创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\Executor.java
public interface Executor {
    //泛型方法
    public <T> T query(String statement, Object parameter);
}
  1. 创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcExecutor.java
public class NlcExecutor implements Executor {

    //属性
    private NlcConfiguration nlcConfiguration =
            new NlcConfiguration();

    // 根据 sql 查找结果
    @Override
    public <T> T query(String sql, Object parameter) {
        //得到连接Connection
        Connection connection = getConnection();
        //查询返回的结果集
        ResultSet set = null;
        PreparedStatement pre = null;

        try {
            pre = connection.prepareStatement(sql);
            //设置参数, 如果参数多, 可以使用数组处理.
            pre.setString(1, parameter.toString());
            set = pre.executeQuery();
            //把set数据封装到对象-monster
            //说明: 这里做了简化处理
            //认为返回的结果就是一个monster记录
            //完善的写法是一套反射机制.
            Monster monster = new Monster();

            //遍历结果集, 把数据封装到monster对象
            while (set.next()) {
                monster.setId(set.getInt("id"));
                monster.setName(set.getString("name"));
                monster.setEmail(set.getString("email"));
                monster.setAge(set.getInt("age"));
                monster.setGender(set.getInt("gender"));
                monster.setBirthday(set.getDate("birthday"));
                monster.setSalary(set.getDouble("salary"));
            }
            return (T) monster;

        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                if (set != null) {
                    set.close();
                }
                if (pre != null) {
                    pre.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception throwables) {
                throwables.printStackTrace();
            }
        }

        return null;
    }
    
     //编写方法,通过NlcConfiguration对象,返回连接
    private Connection getConnection() {
        Connection connection =   nlcConfiguration.build("nlc_mybatis.xml");
        return connection;
    }
}

完成测试

  1. 修改nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java , 增加测试方法
@Test
    public void query() {
        Executor executor = new NlcExecutor();
        Monster monster =
                executor.query("select * from monster where id=?", 1);
        System.out.println("monster-- " + monster);
    }
  1. 测试效果

😄总结

  1. 了解底层的机制可以帮助我们更好的学习,阅读优秀的源码可以增长我们的功力。
  2. 适当的debug可以解决我们的疑惑,底层是一个非常庞大的集成,把握主干就可以了。
  3. 过于追根究底只会影响自己的心绪,会耗费大量时间精力。
  4. 如果自己感兴趣的话,可以多研究一下,会发现其中的乐趣,点到即止。

文章到这里就结束了,如果有什么疑问的地方请指出,诸大佬们一起来评论区一起讨论😁
希望能和诸大佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/53834.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 SQL:从基本查询到高级聚合

目录 背景理论知识示例1211. 查询结果的质量和占比&#xff08;Round group by&#xff09;1204. 最后一个能进入巴士的人 &#xff08;Having limit order by&#xff09;1193. 每月交易 I&#xff08;if group by&#xff09;1179. 重新格式化部门表1174. 即时食物配送 II&am…

jenkins 配置git

在linux 中输入 保证git 安装成功 git --version使用查看git 安装目录&#xff08;非源码安装直接用yum 安装的&#xff09; which gitjenkins 中到 系统管理–>全局工具配置–> Git installations 新建一个项目 选择自由风格 源码管理选择 git 如果使用的是码云&a…

【机器学习】Overfitting and Regularization

Overfitting and Regularization 1. 过拟合添加正则化2. 具有正则化的损失函数2.1 正则化线性回归的损失函数2.2 正则化逻辑回归的损失函数 3. 具有正则化的梯度下降3.1 使用正则化计算梯度&#xff08;线性回归 / 逻辑回归&#xff09;3.2 正则化线性回归的梯度函数3.3 正则化…

论文笔记--GloVe: Global Vectors for Word Representation

论文笔记--GloVe: Global Vectors for Word Representation 1. 文章简介2. 文章概括3 文章重点技术3.1 两种常用的单词向量训练方法3.2 GloVe3.3 模型的复杂度 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;GloVe: Global Vectors for Word Representa…

计算机图形学笔记2-Viewing 观测

观测主要解决的问题是如何把物体的三维“模型”变成我们在屏幕所看到的二维“图片”&#xff0c;我们在计算机看到实体模型可以分成这样几步&#xff1a; 相机变换(camera transformation)或眼变换(eye transformation)&#xff1a;想象把相机放在任意一个位置来观测物体&#…

OSPF协议RIP协议+OSPF实验(eNSP)

本篇博客主要讲解单区域的ospf&#xff0c;多区域的仅作了解。 目录 一、OSPF路由协议概述 1.内部网关协议和外部网关协议 二、OSPF的应用环境 1.从以下几方面考虑OSPF的使用 2.OSPF的特点 三、OSPF重要基本概念 3.1&#xff0c;辨析邻居和邻接关系以及七种邻居状态 3…

MySQL中的函数

系列文章目录 MySQL常见的几种约束 文章目录 系列文章目录前言一、单行函数1.字符串函数 &#xff08;String StringBuilder&#xff09;2.数值函数 &#xff08;Math&#xff09;3.日期与时间函数4.流程函数&#xff08; IF SWITCH&#xff09;5.JSON函数6.其他函数 二、多行…

shopee,lazada,etsy店群如何高效安全的管理

对于电商卖家来说&#xff0c;要经营多个店铺&#xff0c;管理多个账号是非常常见的操作。为了避免账号关联被平台识别出来&#xff0c;需要使用防关联的浏览器来进行操作 ​1、支持多平台 支持同时管理多个电商平台店铺&#xff0c;Shopee、Lazada、etsy、poshmark、vinted等&…

Pytorch 最全入门介绍,Pytorch入门看这一篇就够了

本文通过详细且实践性的方式介绍了 PyTorch 的使用&#xff0c;包括环境安装、基础知识、张量操作、自动求导机制、神经网络创建、数据处理、模型训练、测试以及模型的保存和加载。 1. Pytorch简介 在这一部分&#xff0c;我们将会对Pytorch做一个简单的介绍&#xff0c;包括它…

MFC第二十四天 使用GDI对象画笔和画刷来开发控件(分页控件选择态的算法分析、使用CToolTipCtrl开发动静态提示)

文章目录 GDI对象画笔和画刷来开发控件梯形边框的按钮控件CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp 矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp: 实现文件 矩形边框的三态按钮控件 CToolTipCtrl开发动…

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(六)

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python面试专栏&#xff1a;《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; …

echart折线图,调节折线点和y轴的间距(亲测可用)

options代码&#xff1a; options {tooltip: {trigger: axis, //坐标轴触发&#xff0c;主要在柱状图&#xff0c;折线图等会使用类目轴的图表中使用。},xAxis: {type: category,//类目轴&#xff0c;适用于离散的类目数据&#xff0c;为该类型时必须通过 data 设置类目数据。…

抓紧收藏,Selenium无法定位元素的几种解决方案

01、frame/iframe表单嵌套 WebDriver只能在一个页面上对元素识别与定位&#xff0c;对于frame/iframe表单内嵌的页面元素无法直接定位。 解决方法&#xff1a; driver.switch_to.frame(id/name/obj) switch_to.frame()默认可以直接取表单的id或name属性。如果没有可用的id和…

自适应大屏

可视化大屏适配/自适应现状 可视化大屏的适配是一个老生常谈的话题了&#xff0c;现在其实不乏一些大佬开源的自适应插件、工具但是我为什么还要重复造轮子呢&#xff1f;因为目前市面上适配工具每一个都无法做到完美的效果&#xff0c;做出来的东西都差不多&#xff0c;最终实…

ETHERNET/IP 转ETHERCAT连接ethercat总线伺服如何控制

捷米JM-EIP-ECAT网关连接到ETHERNET/IP总线中做为从站使用&#xff0c;连接到ETHERCAT总线中做为从站使用&#xff0c;可以同时满足多种工业生产的需求。支持广泛的设备类型&#xff0c;可以和多种不同的设备进行通讯。 技术参数 ETHERNET/IP 技术参数 网关做为 ETHERNET/IP …

CentOS下 Docker、Docker Compose 的安装教程

Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 Docker Compose是用于定义…

一些类型推导相关的功能(C++)

目录 auto关键的新用法&#xff08;C11起&#xff09; 背景介绍 用法示例 注意事项 typeid运算符 type_info类 typeid的用法 decltype运算符 用法阐述 用法示例 用法建议 function类模板 用法阐述 用法示例 function较函数指针的优势 std::function和decltype的…

DID以及社交网络中的ZKP

1. 引言 本文关键术语为&#xff1a; Decentralized Identity (DID&#xff0c;去中心化身份) or self-sovereign identity (SSI&#xff0c;自治身份) &#xff1a;是一个基于开放标准的框架&#xff0c;使用自主、独立的标识符和可验证证书&#xff0c;实现可信的数据交换。…

Vue2 第六节 key的作用与原理

&#xff08;1&#xff09;虚拟DOM &#xff08;2&#xff09;v-for中的key的作用 一.虚拟DOM 1.虚拟DOM就是内存中的数据 2.原生的JS没有虚拟DOM: 如果新的数据和原来的数据有重复数据&#xff0c;不会在原来的基础上新加数据&#xff0c;而是重新生成一份 3. Vue会有虚拟…

驶向专业:嵌入式开发在自动驾驶中的学习之道

导语: 自动驾驶技术在汽车行业中的快速发展为嵌入式开发领域带来了巨大的机遇。作为自动驾驶的核心组成部分&#xff0c;嵌入式开发在驱动汽车的智能化和自主性方面发挥着至关重要的作用。本文将探讨嵌入式开发的学习方向、途径以及未来在自动驾驶领域中的展望。 一、学习方向:…
最新文章