手写mybatis之Mapper XML的解析和注册使用

news2024/11/29 10:53:21

前言


你是怎么面对功能迭代的?
很多程序员在刚开始做编程或者新加入一家公司时,都没有多少机会可以做一个新项目,大部分时候都是在老项目上不断的迭代更新。在这个过程你可能要学习N个前人留下的各式各样的风格迥异的代码片段,在这些纵横交错的流程中,找到一席之地,把自己的ifelse加进去。
但说回来,其实不能逐步清理一片屎山,让代码在你的手上逐步清晰、整洁、干净,很多时候也是作为码农自身经验的不足,不懂得系统重构、不了解设计原则、不熟悉业务背景、不清楚产品走向等等原因造成的。所以最好的办法是提升自身的能力,每接到一次需求都有一些技术上的改变,既然它是屎山,那就当做打怪升级了,修一点、改一块、补一片,总会在你手上越来越易于维护和扩展的。
设计
上一章节我们使用了 MapperRegistry 对包路径进行扫描注册映射器,并在 DefaultSqlSession 中进行使用。那么在我们可以把这些命名空间、SQL描述、映射信息统一维护到每一个 DAO 对应的 Mapper XML 的文件以后,其实 XML 就是我们的源头了。通过对 XML 文件的解析和处理就可以完成 Mapper 映射器的注册和 SQL 管理。
在这里插入图片描述

XML 解析和注册类实现关系
在这里插入图片描述
1:SqlSessionFactoryBuilder 作为整个 Mybatis 的入口,提供建造者工厂,包装 XML 解析处理,并返回对应 SqlSessionFactory 处理类。
2:通过解析把 XML 信息注册到 Configuration 配置类中,再通过传递 Configuration 配置类到各个逻辑处理类里,包括 DefaultSqlSession 中,这样就可以在获取映射器和执行SQL的时候,从配置类中拿到对应的内容了。
构建SqlSessionFactory建造者工厂

package com.lm.mybatis.session;

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(Reader reader) {
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
        return build(xmlConfigBuilder.parse());
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }


}

package com.lm.mybatis.builder.xml;

import com.lm.mybatis.builder.BaseBuilder;
import org.dom4j.Element;

public class XMLConfigBuilder extends BaseBuilder {

    private Element root;

    public XMLConfigBuilder(Reader reader) {
        // 1. 调用父类初始化Configuration
        super(new Configuration());
        // 2. dom4j 处理 xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(new InputSource(reader));
            root = document.getRootElement();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析配置;类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器
     *
     * @return Configuration
     */
    public Configuration parse() {
        try {
            // 解析映射器
            mapperElement(root.element("mappers"));
        } catch (Exception e) {
            throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
        return configuration;
    }

    private void mapperElement(Element mappers) throws Exception {
        List<Element> mapperList = mappers.elements("mapper");
        for (Element e : mapperList) {
            String resource = e.attributeValue("resource");
            Reader reader = Resources.getResourceAsReader(resource);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(new InputSource(reader));
            Element root = document.getRootElement();
            //命名空间
            String namespace = root.attributeValue("namespace");

            // SELECT
            List<Element> selectNodes = root.elements("select");
            for (Element node : selectNodes) {
                String id = node.attributeValue("id");
                String parameterType = node.attributeValue("parameterType");
                String resultType = node.attributeValue("resultType");
                String sql = node.getText();

                // ? 匹配
                Map<Integer, String> parameter = new HashMap<>();
                Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                Matcher matcher = pattern.matcher(sql);
                for (int i = 1; matcher.find(); i++) {
                    String g1 = matcher.group(1);
                    String g2 = matcher.group(2);
                    parameter.put(i, g2);
                    sql = sql.replace(g1, "?");
                }

                String msId = namespace + "." + id;
                String nodeName = node.getName();
                SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
                MappedStatement mappedStatement = new MappedStatement.Builder(configuration, msId, sqlCommandType, parameterType, resultType, sql, parameter).build();
                // 添加解析 SQL
                configuration.addMappedStatement(mappedStatement);
            }

            // 注册Mapper映射器
            configuration.addMapper(Resources.classForName(namespace));
        }
    }

}

package com.lm.mybatis.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class Resources {

    public static Reader getResourceAsReader(String resource) throws IOException {
        return new InputStreamReader(getResourceAsStream(resource));
    }

    private static InputStream getResourceAsStream(String resource) throws IOException {
        ClassLoader[] classLoaders = getClassLoaders();
        for (ClassLoader classLoader : classLoaders) {
            InputStream inputStream = classLoader.getResourceAsStream(resource);
            if (null != inputStream) {
                return inputStream;
            }
        }
        throw new IOException("Could not find resource " + resource);
    }

    private static ClassLoader[] getClassLoaders() {
        return new ClassLoader[]{
                ClassLoader.getSystemClassLoader(),
                Thread.currentThread().getContextClassLoader()};
    }

    public static Class<?> classForName(String className) throws ClassNotFoundException {
        return Class.forName(className);
    }

}

package com.lm.mybatis.mapping;

import com.lm.mybatis.session.Configuration;

import java.util.Map;

public class MappedStatement {
    private Configuration configuration;
    private String id;
    private SqlCommandType sqlCommandType;

    private String parameterType;
    private String resultType;
    private String sql;
    private Map<Integer, String> parameter;

    MappedStatement() {
        // constructor disabled
    }

    /**
     * 建造者
     */
    public static class Builder {

        private MappedStatement mappedStatement = new MappedStatement();

        public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, String parameterType, String resultType, String sql, Map<Integer, String> parameter) {
            mappedStatement.configuration = configuration;
            mappedStatement.id = id;
            mappedStatement.sqlCommandType = sqlCommandType;
            mappedStatement.parameterType = parameterType;
            mappedStatement.resultType = resultType;
            mappedStatement.sql = sql;
            mappedStatement.parameter = parameter;
        }

        public MappedStatement build() {
            assert mappedStatement.configuration != null;
            assert mappedStatement.id != null;
            return mappedStatement;
        }

    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public SqlCommandType getSqlCommandType() {
        return sqlCommandType;
    }

    public void setSqlCommandType(SqlCommandType sqlCommandType) {
        this.sqlCommandType = sqlCommandType;
    }

    public String getParameterType() {
        return parameterType;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public Map<Integer, String> getParameter() {
        return parameter;
    }

    public void setParameter(Map<Integer, String> parameter) {
        this.parameter = parameter;
    }

}

package com.lm.mybatis.mapping;

public enum SqlCommandType {
    /**
     * 未知
     */
    UNKNOWN,
    /**
     * 插入
     */
    INSERT,
    /**
     * 更新
     */
    UPDATE,
    /**
     * 删除
     */
    DELETE,
    /**
     * 查找
     */
    SELECT;

}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lm.test.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.lm.test.pojo.User">
        SELECT id, userId, userHead, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>

<?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>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
    </mappers>

</configuration>

package com.lm.test;

import com.lm.mybatis.binding.MapperProxyFactory;
import com.lm.mybatis.binding.MapperRegistry;
import com.lm.mybatis.io.Resources;
import com.lm.mybatis.session.SqlSession;
import com.lm.mybatis.session.SqlSessionFactory;
import com.lm.mybatis.session.SqlSessionFactoryBuilder;
import com.lm.mybatis.session.defaults.DefaultSqlSessionFactory;
import com.lm.test.dao.IUserDao;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;

public class TestApi {
    private Logger logger = LoggerFactory.getLogger(TestApi.class);

    @Test
    public void test_SqlSessionFactory() throws IOException {
        // 1. 从SqlSessionFactory中获取SqlSession
        Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 2. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        // 3. 测试验证
        String res = userDao.queryUserInfoById("10001");
        logger.info("测试结果:{}", res);
    }

}

package com.lm.test.pojo;

import java.util.Date;

public class User {
    private Long id;
    private String userId;          // 用户ID
    private String userHead;        // 头像
    private Date createTime;        // 创建时间
    private Date updateTime;        // 更新时间

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserHead() {
        return userHead;
    }

    public void setUserHead(String userHead) {
        this.userHead = userHead;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

}

在这里插入图片描述

好了到这里就结束了手写mybatis之Mapper XML的解析和注册使用的学习,大家一定要跟着动手操作起来。需要的源码的 可si我获取;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【杂谈一之概率论】CDF、PDF、PMF和PPF概念解释与分析

一、概念解释 1、CDF&#xff1a;累积分布函数&#xff08;cumulative distribution function&#xff09;&#xff0c;又叫做分布函数&#xff0c;是概率密度函数的积分&#xff0c;能完整描述一个实随机变量X的概率分布 2、PDF&#xff1a;连续型概率密度函数&#xff08;p…

平面电磁波的电场能量磁场能量密度相等,能量密度的体积分等于能量,注意电场能量公式也没有复数形式(和坡印廷类似)

1、电场能量密度和磁场能量密度相等(实数场算的) 下面是电场能量密度和磁场能量密度的公式&#xff0c;注意这可不是坡印廷定理。且电场能量密度没有复数表达式&#xff0c;即不是把E和D换成复数形式就行的。注意&#xff0c;一个矢量可以转化为复数形式&#xff0c;两个矢量做…

数据挖掘-padans初步使用

目录标题 Jupyter Notebook安装启动 Pandas快速入门查看数据验证数据建立索引数据选取⚠️注意&#xff1a;排序分组聚合数据转换增加列绘图line 或 **&#xff08;默认&#xff09;&#xff1a;绘制折线图。bar&#xff1a;绘制条形图。barh&#xff1a;绘制水平条形图。hist&…

Discord:报错:A fatal Javascript error occured(解决办法)

按 Windows 键 R 并输入 %appdata% 选择 discord 文件夹并将其删除。 再次按 Windows 键 R 并输入 %LocalAppData% 选择 discord 文件夹并再次将其删除。 附加&#xff1a; 如果还不行&#xff0c;就通过官网下载吧&#xff0c;这个问题通过epic下载可能会有

图文深入理解Oracle DB企业级集中管理神器-GC的安装和部署

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。今天继续宅继续写。 本文承接上篇&#xff0c;介绍GC的安装和部署。咱们不急&#xff0c;慢慢来&#xff0c;饭要一口一口地吃才能吃得踏实自然。 限于篇幅&#xff0c;本节将重点介绍关键步…

【ubuntu】apt是什么

目录 1.apt简介 2.常用apt指令 2.1安装 2.2更新列表 2.3更新已经安装的软件包 2.4搜索软件包 2.5显示软件包信息 2.6移除软件包 2.7清理无用的安装包 2.8清理无用的依赖项 3.apt和apt-get 3.1区别 3.2 总结 1.apt简介 apt的全称是advanced package …

JAVA的三大特性-封装、继承、多态

Java作为一种面向对象的编程语言&#xff0c;其核心特性包括封装、继承和多态。这三大特性是Java语言的基石&#xff0c;它们相互关联&#xff0c;共同构成了Java强大的面向对象能力。 封装&#xff08;Encapsulation&#xff09; 封装是面向对象编程的一个重要概念&#xff0c…

Pytorch最最适合研究生的入门教程,Q3 开始训练

文章目录 Pytorch最最适合研究生的入门教程Q3 开始训练3.1 训练的见解3.2 Pytorch基本训练框架work Pytorch最最适合研究生的入门教程 Q3 开始训练 3.1 训练的见解 如何理解深度学习能够完成任务&#xff1f; 考虑如下回归问题 由函数 y f ( x ) yf(x) yf(x)采样得到的100个…

现在的新电脑在任务管理器里又多了个NPU?它是啥?

前言 今年中旬各家品牌的新笔记本感觉上都是很不错&#xff0c;搞得小白自己心痒痒&#xff0c;突然间想要真的买一台Windows笔记本来耍耍了。 但今天这个文章并不是什么商品宣传啥的&#xff0c;而是小白稍微尝试了一下新笔记本之后的一些发现。 在今年的新笔记本上都多了一…

【GESP】C++一级练习BCQM3025,输入-计算-输出-6

题型与BCQM3024一样&#xff0c;计算逻辑上稍微复杂了一点点&#xff0c;代码逻辑没变&#xff0c;仍属于小学3&#xff0c;4年级的题目水平。 题解详见&#xff1a;https://www.coderli.com/gesp-1-bcqm3025/ https://www.coderli.com/gesp-1-bcqm3025/https://www.coderli.c…

数据提取之JSON与JsonPATH

第一章 json 一、json简介 json简单说就是javascript中的对象和数组&#xff0c;所以这两种结构就是对象和数组两种结构&#xff0c;通过这两种结构可以表示各种复杂的结构 > 1. 对象&#xff1a;对象在js中表示为{ }括起来的内容&#xff0c;数据结构为 { key&#xff1…

最新版本SkyWalking【10.1.0】部署

这里写目录标题 前言前置条件启动Skywalking下载解压启动说明 集成Skywalking Agent下载Agent在IDEA中添加agent启动应用并访问SpringBoot接口 说明 前言 基于当前最新版10.1.0搭建skywalking 前置条件 装有JDK11版本的环境了解SpringBoot相关知识 启动Skywalking 下载 地…

浑元换算策略和武德换算策略-《分析模式》漫谈36

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 “Analysis Patterns”的第3章有这么一句&#xff1a; A conversion, however deterministic, does not follow that faithfully. 2004&#xff08;机械工业出版社&#xff09;中译本…

HTB:Explosion[WriteUP]

目录 连接至HTB服务器并启动靶机 1.What does the 3-letter acronym RDP stand for? 2.What is a 3-letter acronym that refers to interaction with the host through a command line interface? 3.What about graphical user interface interactions? 4.What is the…

【MySQL 08】复合查询

目录 1.准备工作 2.多表查询 笛卡尔积 多表查询案例 3. 自连接 4.子查询 1.单行子查询 2.多行子查询 3.多列子查询 4.在from子句中使用子查询 5.合并查询 1.union 2.union all 1.准备工作 如下三个表&#xff0c;将作为示例&#xff0c;理解复合查询 EMP员工表…

深入探究:在双链表的前面进行插入操作的顺序

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd;惟有主动付出&#xff0c;才有丰富的果…

一次解决Go编译问题的经过

用Go语言编写了一个小的项目&#xff0c;项目开发环境是在本地的Windows环境中&#xff0c;一切单元测试和集成测试通过后&#xff0c;计划将项目部署到VPS服务器上自动运行&#xff0c;但在服务器上执行go run运行时&#xff0c;程序没有任何响应和回显&#xff0c;甚至main函…

有没有一款软件,可以在二楼电脑直接唤醒三楼的电脑?

前言 今天有个小姐姐找到我&#xff0c;咨询能不能在二楼的电脑直接访问到三楼电脑的资料。 这个肯定是可以的啊&#xff01; 其实事情很简单&#xff0c;只需要弄好共享文件夹这个功能&#xff0c;只要手机、平板或者电脑在同个局域网下&#xff0c;就能访问到三楼电脑里的…

深入理解Dubbo源码核心原理-Part4

现在开始研究&#xff0c;消费端真正调用proxy的方法时&#xff0c;走的rpc调用 接下来就要走client&#xff0c;发送request请求了 Dubbo协议是怎样的呢&#xff1f; 具体每个字段什么含义请参照官网 链接&#xff1a;Dubbo协议头含义 编码器按照Dubbo协议来进行编码请求 Ne…

JVM内存回收机制

目录 1.JVM运行时数据区 2.JVM类加载过程 3.双清委派模型 4.垃圾回收机制&#xff08;GC&#xff09; 找出谁是垃圾方案一&#xff1a;引用计数 找出谁是垃圾&#xff1a;方案二&#xff0c;可达性分析 释放垃圾的内存空间 判断垃圾&#xff1a;jvm依据对象的年龄对 对象…