揭开神秘的面纱--自己实现MyBatis 底层机制[下]

news2024/9/29 9:29:54

😀前言
本篇博文是MyBatis 底层机制的核心实现,简单的手法让MyBatis 不再神秘,希望能够帮助到你😊

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

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

文章目录

  • 自己实现MyBatis 底层机制[下]
    • 实现任务阶段3- 将Sqlsession 封装到执行器
      • 分析示意图
      • 代码实现
      • 完成测试
    • 实现任务阶段4- 开发Mapper 接口和Mapper.xml
      • 分析【示意图】
      • 代码实现
    • 实现任务阶段5- 开发和Mapper 接口相映射的MapperBean
      • 分析示意图
      • 代码实现
    • 实现任务阶段6
      • 运行结果示意
      • 代码实现
      • 完成测试
    • 实现任务阶段7- 实现动态代理Mapper 的方法
      • 分析示意图
      • 代码实现
      • 完成测试
    • 😄总结

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

实现任务阶段3- 将Sqlsession 封装到执行器

分析示意图

先观察原生MyBatis 的SqlSession 接口和默认实现

代码实现

  1. 创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcSqlsession.java
/**
 * NlcSqlSession: 搭建Configuration(连接)和Executor之间桥梁
 * 这里有操作DB的方法-
 */
public class NlcSqlSession {
    //属性
    //执行器
    private Executor executor = new NlcExecutor();
    //配置
    private NlcConfiguration nlcConfiguration =
            new NlcConfiguration();

    //编写方法SelectOne 返回一条记录-对象 [做了简化]
    //说明: 在原生的Mybatis中 statement 不是sql ,而是要执行的接口方法
    public <T> T selectOne(String statement, Object parameter) {
        return executor.query(statement, parameter);
    }
}

完成测试

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

实现任务阶段4- 开发Mapper 接口和Mapper.xml

分析【示意图】

代码实现

1 、创建nlc-mybatis\src\main\java\com\nlc\mapper\MonsterMapper.java

//MonsterMapper: 声明对db的crud方法
public interface MonsterMapper {

    //查询方法
    public Monster getMonsterById(Integer id);
}

2、创建nlc-mybatis\src\main\resources\MonsterMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.nlc.mapper.MonsterMapper">
    <!--实现配置接口方法getMonsterById-->
    <select id="getMonsterById" resultType="com.nlc.entity.Monster">
        select * from monster where id = ?
    </select>
</mapper>

实现任务阶段5- 开发和Mapper 接口相映射的MapperBean

分析示意图

代码实现

1 、创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\config\Function.java , 对应Mapper 的方法信息

// Function: 记录对应的Mapper的方法信息
public class Function {
    //属性
    private String sqlType; //sql类型,比如select,insert,update, delete
    private String funcName; //方法名
    private String sql;//执行的sql语句
    private Object resultType;//返回类型
    private String parameterType;//参数类型

    public String getSqlType() {
        return sqlType;
    }

    public void setSqlType(String sqlType) {
        this.sqlType = sqlType;
    }

    public String getFuncName() {
        return funcName;
    }

    public void setFuncName(String funcName) {
        this.funcName = funcName;
    }

    public String getSql() {
        return sql;
    }

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

    public Object getResultType() {
        return resultType;
    }

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

    public String getParameterType() {
        return parameterType;
    }

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

    @Override
    public String toString() {
        return "Function{" +
                "sqlType='" + sqlType + '\'' +
                ", funcName='" + funcName + '\'' +
                ", sql='" + sql + '\'' +
                ", resultType=" + resultType +
                ", parameterType='" + parameterType + '\'' +
                '}';
    }
}

2 、创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\config\MapperBean.java, 将Mapper 的信息,进行封装

//MapperBean: 将Mapper信息,进行封装
public class MapperBean {
    private String interfaceName;//接口名

    //接口下的所有方法-集合
    private List<Function> functions;

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public List<Function> getFunctions() {
        return functions;
    }

    public void setFunctions(List<Function> functions) {
        this.functions = functions;
    }
}

实现任务阶段6

任务:在NlcConfiguration, 读取XxxMapper.xml,能够创建MappperBean对象。

运行结果示意

mapperBean--MapperBean{interfaceName='com.nlc.mapper.MonsterMapper',
list=[Function{sqlType='select',
funcName='getMonsterById',
sql='select * from monster where id=?', 
resultType=com.nlc.mapper.MonsterMapper,
parameterType='null'}]}

代码实现

  1. 修改nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcConfiguration.java, 增加方法
  public MapperBean readMapper(String path) {
        MapperBean mapperBean = new MapperBean();
        try {
            //获取到xml文件对应的InputStream
            InputStream stream = loader.getResourceAsStream(path);
            SAXReader reader = new SAXReader();
            //获取到xml文件对应的document-dom4j
            Document document = reader.read(stream);
            //得到xml文件的根元素/根节点
            /*
            <mapper namespace="com.nlc.mapper.MonsterMapper">
                <!--实现配置接口方法getMonsterById-->
                <select id="getMonsterById" resultType="com.nlc.entity.Monster">
                    select * from monster where id = ?
                </select>
            </mapper>
             */
            Element root = document.getRootElement();

            //获取到namespace
            String namespace = root.attributeValue("namespace").trim();
            //设置mapperBean的属性interfaceName
            mapperBean.setInterfaceName(namespace);
            //得到root的迭代器-可以遍历它的子节点/子元素-生成Function
            Iterator rootIterator = root.elementIterator();
            //保存接口下所有的方法信息
            List<Function> list = new ArrayList<>();
            //遍历它的子节点/子元素-生成Function
            while (rootIterator.hasNext()) {
                //取出一个子元素- dom4j Element
                /**
                 * <select id="getMonsterById" resultType="com.nlc.entity.Monster">
                 *                     select * from monster where id = ?
                 *  </select>
                 */
                Element e = (Element) rootIterator.next();
                Function function = new Function();
                String sqlType = e.getName().trim();
                String funcName = e.attributeValue("id").trim();
                //resultType是返回类型的全路径-即全类名
                String resultType = e.attributeValue("resultType").trim();
                String sql = e.getText().trim();
                //开始封装
                function.setSql(sql);
                function.setFuncName(funcName);
                function.setSqlType(sqlType);
                //这里 function-private Object resultType; 是resultType实例
                //使用反射生成一个对象, setResultType

                Object newInstance = Class.forName(resultType).newInstance();
                function.setResultType(newInstance);

                //将封装好的function对象加入到 list
                list.add(function);

            }
            //while循环结束后, 将function的list设置
            mapperBean.setFunctions(list);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return mapperBean;
    }

完成测试

1 、修改nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java, 增加测试方法

  @Test
    public void readMapper() {

        NlcConfiguration nlcConfiguration = new NlcConfiguration();
        MapperBean mapperBean =
                nlcConfiguration.readMapper("MonsterMapper.xml");
        System.out.println("mapperBean---" + mapperBean);
    }

2、测试效果
数据表没有填充数据,不影响测试

实现任务阶段7- 实现动态代理Mapper 的方法

分析示意图

代码实现

1 、创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcMapperProxy.java

//NlcMapperProxy: 动态代理生成Mapper对象,调用NlcExecutor方法
public class NlcMapperProxy implements InvocationHandler {
    //属性
    private NlcSqlSession nlcSqlSession;
    private String mapperFile;
    private NlcConfiguration nlcConfiguration;

    //构造器
    public NlcMapperProxy(NlcConfiguration nlcConfiguration, NlcSqlSession nlcSqlSession, Class clazz) {
        this.nlcConfiguration = nlcConfiguration;
        this.nlcSqlSession = nlcSqlSession;
        this.mapperFile = clazz.getSimpleName() + ".xml";
    }

    //当执行Mapper接口的代理对象方法时,会执行到invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MapperBean mapperBean =  nlcConfiguration.readMapper(this.mapperFile);
        //判断是否是xml文件对应的接口
        if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())) {
            return null;
        }
        //取出mapperBean的functions
        List<Function> functions = mapperBean.getFunctions();
        //判断当前mapperBean解析对应MappperXML后 , 有方法
        if (null != functions && 0 != functions.size()) {

            for (Function function : functions) {
                //当前要执行的方法和function.getFuncName()一样
                //说明我们可以从当前遍历的function对象中,取出相应的信息sql, 并执行方法
                if(method.getName().equals(function.getFuncName())) {
                    //如果我们当前的function 要执行的sqlType是select
                    //我们就去执行selectOne
                    /**
                     * 1. 如果要执行的方法是select , 就对应执行selectOne
                     * 2. 因为在NlcSqlSession就写了一个 selectOne
                     * 3. 实际上NlcSqlSession 应该对应不同的方法(多个方法)
                     * , 根据不同的匹配情况调用不同方法, 并且还需要进行参数解析处理, 还有比较复杂的字符串处理,拼接sql ,处理返回类型等等工作
                     * 4. 因为主要是mybatis 生成mapper动态代理对象, 调用方法的机制,所以这里做了简化
                     */
                    if("select".equalsIgnoreCase(function.getSqlType())) {
                        return nlcSqlSession.selectOne(function.getSql(),String.valueOf(args[0]));
                    }
                }
            }
        }
        return null;
    }
}

2 、修改nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcSqlsession.java

    /**
        *解读
        * 1. 当调用getMapper(MonsterMapper.class)
        * 2. 这里的T 就是MonsterMapper
        * 3. 所有接收MonsterMapper monsterMapper =   getMapper(MonsterMapper.class)
        * 4. 在程序中,就可以通过monsterMapper.xxx() 方法
        * 5. 因为monsterMapper 的运行类型是代理类型$Proxy
        * 6. 所以当你调用monsterMapper.xxx(实参) 方法时,是一种动态代理执行,
        *也就是会调用到NlcMapperProxy 这个代理类的 invoke(Object proxy, Method method, Object[] args)
        * 7. 这时Method 就是xxx(实参), proxy 就是对象monsterMapper, args 就是你的实参
     */
    public <T> T getMapper(Class<T> clazz) {
        //返回动态代理对象
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
                new NlcMapperProxy(nlcConfiguration,this,clazz));
    }

3 、创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcSessionFactory.java

//NlcSessionFactory 会话工厂-返回会话NlcSqlSession
public class NlcSessionFactory {
    public static NlcSqlSession openSession() {
        return new NlcSqlSession();
    }
}

完成测试

1 、修改nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java, 增加测试方法

    @Test
    public void openSession() {
        NlcSqlSession nlcSqlSession = NlcSessionFactory.openSession();
        MonsterMapper mapper = nlcSqlSession.getMapper(MonsterMapper.class);
        Monster monster = mapper.getMonsterById(1);
        System.out.println("monster===" + monster);
    }

2、测试效果

😄总结

  1. 很多时候事务的本身并不是可怕的,而是我们自身对于未知事务的恐惧把它扩大了。
  2. MyBatis也是同样,它只是人类创建出来提高效率的工具,他的能力取决于使用的人。
  3. 去学习它、了解它,你会发现没有那么多的未知,得到的都是惊喜。
  4. MyBatis是一款优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集,节省了原生90%的代码。

😁热门专栏推荐
MyBatis手写机制篇–自己实现MyBatis 底层机制–抽丝剥茧(上)

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

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

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

相关文章

JAVA-字符串生成图片

直接上代码 public static void main(String[] args) throws IOException {createFontImage("红色", new Font("宋体", Font.BOLD, 50), 400, 400);}/*** 根据str,font的样式将文字变成图片,然后返回一个流** param str 字符串* param font 字体* pa…

奇富科技引领编制国内首个金融行业大模型标准

7月28日&#xff0c;由中国信息通信研究院&#xff08;以下简称中国信通院&#xff09;组织的“行业大模型高质量发展论坛暨可信AI大模型标准宣贯会”在江苏南京召开。会议发起了行业大模型标准联合推进计划&#xff0c;启动了多个行业的大模型标准编制工作&#xff0c;其中&am…

ES6 - promise.all和race方法的用法详解

文章目录 一、前言二、Promise.all()1&#xff0c;第一句&#xff1a;Promise.all()方法接受一个数组作为参数&#xff0c;且每一个都是 Promise 实例2&#xff0c;第二句&#xff1a;如果不是&#xff0c;就会先调Promise.resolve方法&#xff0c;将参数转为 Promise 实例再进…

Python web实战 | Docker+Nginx部署python Django Web项目详细步骤【干货】

概要 在这篇文章中&#xff0c;我将介绍如何使用 Docker 和 Nginx 部署 Django Web 项目。一步步讲解如何构建 Docker 镜像、如何编写 Docker Compose 文件和如何配置 Nginx。 1. Docker 构建 Django Web 项目 1.1 配置 Django 项目 在开始之前&#xff0c;我们需要有一个 D…

自定义代理

以下代码&#xff1a;可以在view 和model文章中进行添加 m_model new QStandardItemModel(2, FixedColumnCount, this);m_selection new QItemSelectionModel(m_model, this);ui->tableView->setModel(m_model); //设置模型ui->tableView->setSelectionModel(m_s…

第一堂棒球课:MLB棒球大联盟的主要战术·棒球1号位

MLB棒球大联盟的主要战术 攻击战术run-and-foul&#xff08;跑垒战术&#xff09;&#xff1a;以速度为优势&#xff0c;在适当的时机发动进攻&#xff0c;争取在一回合内完成得分。 grounder&#xff08;阻截战术&#xff09;&#xff1a;队员在垒包之间阻止对手的跑垒和传球。…

eclipse 最新版没有navigator视图如何解决

使用project exploere视图可以显示类似navigator视图 1.显示project exploere视图 window---->show view --->project exploere 2.project exploere视图转换为类似navigator视图 第一步&#xff1a;点击视图右上角三个点或者倒三角&#xff0c;点击fiters and custom…

【java入门学习】

文章目录 java学习章节目录学习前的准备main函数怎么写&#xff1f;Java程序运行的步骤如何让代码运行起来&#xff1f;java程序由三部分构成&#xff1a;注释注释规范 标识符练习 总结 java学习章节目录 SE语法 初阶段主要来熟悉java的语法&#xff1a; 1.初始java 2.数据类型…

真正实现无人化自主管理的清洁机器人来了!涂鸦智能新方案为你解密

如果你观察家庭清洁电器的发展历程&#xff0c;可以发现这就是一部人类“偷懒”史&#xff0c;而有技术含量的“懒”&#xff0c;真的可以改变世界&#xff01; 一、懒是产品创新的重要灵感来源 从洗衣机替代手洗&#xff0c;到扫地机器人帮助人们从繁琐的地面清洁中解脱&…

RL 实践(6)—— CartPole【REINFORCE with baseline A2C】

本文介绍 REINFORCE with baseline 和 A2C 这两个带 baseline 的策略梯度方法&#xff0c;并在 CartPole-V0 上验证它们和无 baseline 的原始方法 REINFORCE & Actor-Critic 的优势参考&#xff1a;《动手学强化学习》完整代码下载&#xff1a;7_[Gym] CartPole-V0 (REINFO…

复现YOLOv8改进最新MPDIoU:有效和准确的边界盒回归的损失,打败G/E/CIoU,效果明显!!!

MPDIoU: A Loss for Efficient and Accurate Bounding Box Regression 论文简介MPDIoU核心设计思路论文方法实验部分加入YOLOv5代码论文地址:https://arxiv.org/pdf/2307.07662.pdf 论文简介 边界盒回归(Bounding box regression, BBR)广泛应用于目标检测和实例分割,是目标…

详细介绍如何使用HuggingFace和PyTorch进行医学图像分割-附源码

医学图像分割是一种创新过程,使外科医生能够拥有虚拟的“X 射线视觉”。它是医疗保健领域非常有价值的工具,可提供非侵入性诊断和深入分析。考虑到这一点,在这篇文章中,我们将探索威斯康辛大学麦迪逊分校胃肠道图像分割Kaggle 挑战数据集。作为该项目的一部分,我们将使用 …

python核心-面向对象-三大特性-综合案例

# 定义三个类, 小狗, 小猫, 人 # 小狗: 姓名, 年龄(默认1岁); 吃饭, 玩, 睡觉, 看家(格式: 名字是xx, 年龄xx岁的小狗在xx) # 小猫: 姓名, 年龄(默认1岁); 吃饭, 玩, 睡觉, 捉老鼠(格式: 名字是xx, 年龄xx岁的小猫在xx) # 人: 姓名, 年龄(默认1岁), 宠物; 吃饭…

【替换】批量替换命令文件名称或者文件内容

你是否困扰过&#xff0c;每次文件F2进行修改&#xff0c;十几个没关系&#xff0c;百来个写代码麻烦&#xff0c;那就使用shell命令吧 执行语句 先养成习惯查询验证&#xff0c;因为是批量替换&#xff0c;为了防止全局替换嘎嘎叫 替换文件名 # 【习惯】先使用顾虑查询&…

暴力猴插件简明开发教程->百度首页默认设置为我的关注

文章目录 暴力猴插件开发简明教程->百度首页默认设置为我的关注缘起缘灭思路实现尾声 暴力猴插件开发简明教程->百度首页默认设置为我的关注 缘起 在我的百度首页有很多自己设置的导航链接(接近100个),里面放了我常用的网站, 如下图 但是最近一段时间, 我发现百度做了一…

算法38:反转链表

一、需求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例3&#xff…

电器水族设备加热器UL1018检测报告

UL1018---电气水族设备 适用范围&#xff1a;本标准要求适用于300V或以下&#xff0c;用于室内商业或家用的水族器材上的设备。如加热器、水泵、过滤器、反射器 电线支架等相似用途的产品。 本标准不包括: 不是和水族器材一起用的照明器或台子;水族容器 一、UL1018标准测试内…

R中无法安装GitHub包 | 花费大量时间的小小经验

写在前面 最近在安装R包时&#xff0c;遇到无法安装包的情况&#xff0c;然后自己就各种捣鼓。最后&#xff0c;R环境也会让自己弄崩&#xff0c;如果你遇到此情况R语言无法调用stats.dll的问题解决方案,可以参考我前面的方法捣鼓一下。 那么&#xff0c;这个推文也是记录自己…

【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface

文章目录 前言官方解释内存布局程序运行中的内存布局通用方法 前言 在 Go 中&#xff0c;如果 interface{} 作为函数参数的话&#xff0c;是可以传任意参数的&#xff0c;然后通过类型断言来转换。 举个例子&#xff1a; package mainimport "fmt"func foo(v inter…

2023年自然语言处理与信息检索国际会议(ECNLPIR 2023) | EI Compendex, Scopus双检索

会议简介 Brief Introduction 2023年自然语言处理与信息检索国际会议(ECNLPIR 2023) 会议时间&#xff1a;2023年9月22日-24日 召开地点&#xff1a;中国杭州 大会官网&#xff1a;ECNLPIR 2023-2023 Eurasian Conference on Natural Language Processing and Information Retr…