MyBatis的底层机制

手写MyBatis底层机制

读取配置文件,得到数据库连接

思路

  1. 引入必要的依赖
  2. 需要写一个自己的config.xml文件,在里面配置一些信息,driver,url ,password,username
  3. 需要编写Configuration类,对 自己的config.xml文件 进行解析,得到一个数据库连接

实现

  • 引入必要的依赖
<dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
  • 需要写一个自己的config.xml文件,在里面配置一些信息,driver,url ,password,username
<?xml version="1.0" encoding="UTF-8" ?>
<database>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/hsp_mybatis?
        useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="zy"/>
</database>
  • 需要编写Configuration类,对 自己的config.xml文件 进行解析,得到一个数据库连接
public class ZyConfiguration {

    //属性 类加载器
    private ClassLoader classLoader =
            ClassLoader.getSystemClassLoader();

    //读取xml文件信息并处理
    public Connection build(String resource) {
        Connection connection = null;
        
        //加载配置文件,获取对应的InputStream流
        InputStream resourceAsStream =
                classLoader.getResourceAsStream(resource);

        //解析xml文件
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(resourceAsStream);
            Element root = document.getRootElement();

            //解析rootElement
            System.out.println("root= "+root);
            return evalDataSource(root);

        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

    }


    //解析xml文件 并返回一个连接
    private Connection evalDataSource(Element node) {
        Iterator property = node.elementIterator("property");
        String driverClassName = null;
        String url = null;
        String username = null;
        String password = null;

        //遍历node子节点 获取属性值
        while(property.hasNext()){
            Element pro = (Element)property.next();
            String name = pro.attributeValue("name");
            String value = pro.attributeValue("value");

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

        }
        Connection connection = null;
        try {
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url, username, password);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return connection;
    }

}

编写执行器,输入SQL语句,完成操作

思路

  1. 需要写一个实体类,对应monster表
  2. 编写接口executor
  3. 实现接口,编写自己的执行器
  4. 需要一个 自己的Configuration类 返回连接,通过连接对数据库进行操作

实现

  • 需要写一个实体类,对应monster表
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Monster {
    private Integer id;
    private Integer age;
    private String name;
    private String email;
    private Date birthday;
    private double salary;
    private Integer gender;
}
  • 编写接口executor
public interface Executor {
    public <T> T query(String statement,Object parameter);
}

  • 实现接口,编写自己的执行器
public class ZyExecutor implements Executor{

    private ZyConfiguration zyConfiguration = new ZyConfiguration();

    @Override
    public <T> T query(String sql, Object parameter) {
        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 = 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 (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                if (set != null) {
                    set.close();
                }
                if (pre != null) {
                    pre.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        
    }

    public Connection getConnection(){//Configuration类 返回连接,通过连接对数据库进行操作
       return zyConfiguration.build("zy_mybatis.xml");
    }
}
  • 需要一个 自己的Configuration类 返回连接,通过连接对数据库进行操作

将Sqlsession封装到执行器

思路

  1. 需要写自己的Sqlsession类,它是搭建连接和执行器之间的桥梁,里面封装有 执行器 和 配置文件 以及 操作DB 的具体方法
  2. 写一个selectOne方法 ,SelectOne() 返回一条记录,一条记录对应一个Monster对象
实现
  • 需要写自己的Sqlsession类,它是搭建连接和执行器之间的桥梁,里面封装有 执行器 和 配置文件 以及 操作DB 的具体方法
public class ZySqlSession {//搭建连接和执行器之间的桥梁

    //执行器
    private Executor executor = new ZyExecutor();

    //配置
    private ZyConfiguration zyConfiguration = new ZyConfiguration();

    //操作DB 的具体方法
    //SelectOne 返回一条记录-对象
    public <T> T selectOne(String statement,Object parameter){
        return executor.query(statement,parameter);
    }
}
  • 写一个selectOne方法 ,SelectOne() 返回一条记录,一条记录对应一个Monster对象
//操作DB 的具体方法
    //SelectOne 返回一条记录-对象
    public <T> T selectOne(String statement,Object parameter){
        return executor.query(statement,parameter);
    }

开发Mapper接口和Mapper.xml

思路

  1. 编写MonsterMapper接口,里面有方法getMonsterById(Integer id)根据id返回一个monster对象
  2. 在resources下编写对应的monsterMapper.xml(简化:因为在resources 编译时会在类路径下比较好写)
  3. monsterMapper.xml 编写具体的sql语句,并指定语句类型,id,resultType(和原生Mybatis一样)

实现

  • 编写MonsterMapper接口,里面有方法getMonsterById(Integer id)根据id返回一个monster对象
public interface MonsterMapper {
    public Monster getMonsterById(Integer id);
}
  • 在resources下编写对应的monsterMapper.xml(简化:因为在resources 编译时会在类路径下比较好写)
  • monsterMapper.xml 编写具体的sql语句,并指定语句类型,id,resultType(和原生Mybatis一样)
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.code_study.mapper.MonsterMapper">
    <!--    实现配置接口方法 getMonsterById-->
    <select id="getMonsterById" resultType="com.code_study.entity.Monster">
        SELECT * FROM monster WHERE id = ?
    </select>
</mapper>

开发MapperBean,可以和Mapper接口相映射

思路

  1. 开发 Function类 ,用于记录对应的Mapper的方法信息,比如sql类型,方法名,执行的sql语句,返回类型,入参类型
  2. 开发 MapperBean类,记录接口信息和接口下的所有方法
  3. Function类 对应 monsterMapper.xml中的信息
  4. MapperBean类 对应 MonsterMapper 接口中的信息

实现

  • 开发 Function类 ,用于记录对应的Mapper的方法信息,比如sql类型,方法名,执行的sql语句,返回类型,入参类型
//对应 monsterMapper.xml中的信息
public class Function {
    private String sqlType;//sql类型,比如select,insert,update,delete
    private String funcName;//方法名
    private String sql;//执行的sql语句
    private Object resultType;//返回类型
    private String parameterType;//入参类型
}
  • 开发 MapperBean类,记录接口信息和接口下的所有方法
//对应 MonsterMapper 接口中的信息
public class MapperBean {
    private String interfaceName;//接口名

    //    接口下的所有方法
    private List<Function> functions;
}
  • Function类 对应 monsterMapper.xml中的信息
  • MapperBean类 对应 MonsterMapper 接口中的信息

在Configuration中解析MapperXML获取MapperBean对象

思路

  1. 在Configuration 添加方法readMapper(String path)
  2. 通过 path 读取接口对应的Mapper方法
  3. 保存接口下所有的方法信息
  4. 封装成 MapperBean对象

实现

  • 在Configuration 添加方法readMapper(String path)
  • 通过 path 读取接口对应的Mapper方法
  • 保存接口下所有的方法信息
  • 封装成 MapperBean对象
 //解析MapperXML获取MapperBean对象
    //path = xml的路径+文件名 是从类的加载路径计算的(如果放在resource目录下 之间传xml文件名即可)
    public MapperBean readMapper(String path) {
        MapperBean mapperBean = new MapperBean();

        InputStream resourceAsStream = classLoader.getResourceAsStream(path);
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(resourceAsStream);
            Element root = document.getRootElement();

            String namespace = root.attributeValue("namespace");
            mapperBean.setInterfaceName(namespace);

            List<Function> list = new ArrayList<>();//保存接口下所有的方法信息

            //得到root的迭代器
            Iterator iterator = root.elementIterator();
            while(iterator.hasNext()){
                Element e = (Element)iterator.next();
                String sqlType = e.getName().trim();
                String sql = e.getText().trim();
                String funcName = e.attributeValue("id");
                String resultType = e.attributeValue("resultType");

                //ResultType 返回的是一个Object对象 ->反射
                Object instance = Class.forName(resultType).newInstance();

                //封装 function 对象
                Function function = new Function();
                function.setSql(sql);
                function.setSqlType(sqlType);
                function.setFuncName(funcName);
                function.setResultType(instance);

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


                mapperBean.setFunctions(list);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return mapperBean;
    }

动态代理Mapper方法

思路

  1. 在SqlSession中添加方法 getMapper 输入一个Class类型,返回mapper的动态代理对象
  2. 编写动态代理类 实现 InvocationHandler 接口
  3. 取出mapperBean的functions 遍历
  4. 判断 当前要执行的方法和function.getFunctionName是否一致
  5. 调用方法返回 动态代理对象
  6. 编写SqlSessionFactory 会话工厂,可以返回SqlSession

实现

  • 编写动态代理类 实现 InvocationHandler 接口

  • 在SqlSession中添加方法 getMapper 输入一个Class类型,返回mapper的动态代理对象

 //返回mapper的动态代理对象
    public <T> T getMapper(Class<T> clazz){
        return (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{clazz},
                new ZyMapperProxy(zyConfiguration,clazz,this));
    }
  • 取出mapperBean的functions 遍历
  • 判断 当前要执行的方法和function.getFunctionName是否一致
  • 调用方法返回 动态代理对象
public class ZyMapperProxy implements InvocationHandler {
    private ZySqlSession zySqlSession;
    private String mapperFile;
    private ZyConfiguration zyConfiguration;

    public ZyMapperProxy(ZySqlSession zySqlSession, Class clazz, ZyConfiguration zyConfiguration) {
        this.zySqlSession = zySqlSession;
        this.zyConfiguration = zyConfiguration;
        this.mapperFile = clazz.getSimpleName() + ".xml";
    }

    //当执行Mapper接口的代理对象方法时,会执行到invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MapperBean mapperBean = zyConfiguration.readMapper(this.mapperFile);

        //判断是否为当前xml文件对应的接口
        if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())){
            return null;
        }

        //取出mapperBean的functions
        List<Function> functions = mapperBean.getFunctions();
        //判断当前的mapperBean 解析对应的MapperXML后,有方法
        if (null != functions || 0 != functions.size()){
            for (Function function : functions) {
                //当前要执行的方法和function.getFunctionName
                if (method.getName().equals(function.getFuncName())){
                    if ("SELECT".equalsIgnoreCase(function.getSqlType())){
                        return zySqlSession.selectOne(function.getSql(),String.valueOf(args[0]));
                    }
                }
            }
        }

        return null;
    }
}
  • 编写SqlSessionFactory 会话工厂,可以返回SqlSession
public class ZySqlSessionFactory {
    
    public static ZySqlSession open(){
        return new ZySqlSession();
    }
}

测试

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

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

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

相关文章

服务器数据恢复—同品牌不同系列服务器raid5阵列数据恢复方案分析

RAID5磁盘阵列数据恢复案例一&#xff1a; 服务器数据恢复环境&#xff1a; 一台某品牌LH6000系列服务器&#xff0c;通过NetRaid阵列卡将4块硬盘组建为一组RAID5磁盘阵列。操作系统都为Window server&#xff0c;数据库是SQLServer。 服务器故障&#xff1a; LH6000系列服务器…

四、嵌入式技术(考点篇)试题(1)

我选择C&#xff0c;实际答案选B&#xff0c;答案给出的理由是&#xff0c;SoC是片上系统&#xff0c;包含完整系统和嵌入式软件全部内容&#xff0c;B的说法有点片面。 明显选C&#xff0c;嵌入式跟通用性不太沾边。 嵌入式OS特征&#xff1a;裁剪配置安全可靠实时高确定&…

Python基础知识——(001)

文章目录 P4——3. 程序设计语言的分类 1. 程序设计语言 2. 编译与解释 P5——4. Python语言的简介与开发工具 1. Python语言的简介 2. Python语言的发展 3. Python语言的特点 4. Python的应用领域 5. Python的开发工具 P6——5. IPO编程方式 IPO程序编写方法 P7——6. print函…

大模型隐私窃取攻击

前言 对于大模型风险&#xff0c;目前大家更多关注的还是越狱攻击。隐私这一块&#xff0c;可能国内还不如欧美重视&#xff0c;在安全的学术四大会议论文中&#xff0c;有时候甚至AI隐私的论文比AI安全的论文更多。但实际上&#xff0c;除了越狱之外&#xff0c;另外一大风险…

监控电脑软件【2024最新】|6款软件保姆式解析!

在数字化办公日益普及的今天&#xff0c;很多企业为了更好的提升员工的工作效率和保障企业的数据安全&#xff0c;开始给自己的企业布局电脑监控软件。 但市面上的电脑监控软件种类繁多复杂&#xff0c;为了更好的保障企业利用&#xff0c;小编推荐了以下几款电脑监控软件供大…

阶段三:项目开发---大数据开发运行环境搭建:任务4:安装配置Spark集群

任务描述 知识点&#xff1a;安装配置Spark 重 点&#xff1a; 安装配置Spark 难 点&#xff1a;无 内 容&#xff1a; Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。Spark是UC Berkeley AMP lab (加州大学伯克利分校的AMP实验室)所开源的类Hadoop …

Python自动化测试系列[v1.0.0][高效自动化设计]

Python多线程应用于自动化测试 将多线程在测试巧妙地应用&#xff0c;确实会带来很多好处&#xff0c;并且这是充分利用机器资源执行高效率测试很好的方式 # -*- coding: utf-8 -*- import threading from time import ctime import time from selenium import webdriverdef …

【c语言】玩转文件操作

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C语言 目录 引言 一、文件的打开和关闭 1.流 2.标准流 3.文本文件和二进制文件 4.控制文件打开与关闭的函数 二、文件的顺序读写 三、文件的随机读写 1…

7月学术会议:7月可投的EI国际会议

随着科技的迅猛发展&#xff0c;学术交流与研讨成为了推动科研进步的重要途径。进入7月&#xff0c;众多高质量的EI国际会议纷纷拉开帷幕&#xff0c;为全球的科研工作者提供了一个展示研究成果、交流学术思想的平台。以下&#xff0c;我们将详细介绍一些在7月可投的EI国际会议…

Java集合升序降序、转Set的方法

Collections.sort(list,Comparator.comparing(OcApplySquareVo::getApplyName).reversed()); 集合转set /** 集合转set */Set<String> pkCodeSet rows.stream().map(RailwayWeighBookResult.RailwayWeighBook::getPkCode).collect(Collectors.toSet());

猫咪浮毛太多怎么处理?6年铲屎官最值得买的猫毛空气净化器分享

作为一位拥有6年铲屎经验的铲屎官&#xff0c;家中既有宝宝又有毛孩子的铲屎官家庭来说&#xff0c;空气中的宠物异味和猫毛不仅影响生活质量&#xff0c;更关乎家人的健康。普通空气净化器虽然能够提供基本的空气净化&#xff0c;但对于养猫家庭的特定需求&#xff0c;如去除宠…

Pytest单元测试系列[v1.0.0][Pytest基础]

Pytest安装与配置 和Unittest一样&#xff0c;Pytest是另一个Python语言的单元测试框架&#xff0c;与Unittest相比它的测试用例更加容易编写、运行方式更加灵活、报错信息更加清晰、断言写法更简洁并且它可以运行有unittest和nose编写的测试用例。 Pytest 安装 启动命令行&…

A股本周在3000点以下继续筑底,本周依然继续探底?

夜已深&#xff0c;市场传来了3个浓烈的消息&#xff0c;炸锅了&#xff0c;恐有大事发生&#xff0c;马上告诉所有人&#xff1a; 消息面&#xff1a; 1、中国经济周刊首席评论员钮文新称&#xff1a;不要等中小投资者都彻底希望&#xff0c;销户离场了&#xff0c;才发现该…

新恒汇过会一年多注册仍遥遥无期,实控人大额负债入股资金靠借款

《港湾商业观察》施子夫 自2022年6月递表深交所创业板获受理&#xff0c;新恒汇电子股份有限公司 &#xff08;以下简称&#xff0c;新恒汇&#xff09;的上市之路无疑颇显诸多坎坷。2022年7月&#xff0c;深交所下发第一轮审核问询函&#xff1b;同年11月&#xff0c;深交所下…

Bugly的底层是怎么实现的

Bugly 入门 首先&#xff0c;简要介绍什么是 Bugly 以及它的主要功能&#xff1a; Bugly 是什么&#xff1a; Bugly 是腾讯提供的一款移动应用质量监控工具&#xff0c;主要用于捕捉应用的崩溃、ANR&#xff08;应用无响应&#xff09;、卡顿和错误日志。 主要功能&#xff1…

微型导轨如何提升数控机床的稳定性?

数控机床是加工设备中常用的机床&#xff0c;精度和稳定性是衡量数控机床性能的重要指标。而微型导轨作为数控机床中重要的传动元件&#xff0c;数控机床与其具体结构性能是密不可分的&#xff0c;那么微型导轨如何提高数控机床的稳定性呢&#xff1f; 1、微型导轨通过采用先进…

【见刊通知】MVIPIT 2023机器视觉、图像处理与影像技术国际会议

MVIPIT 2023&#xff1a;https://ieeexplore.ieee.org/xpl/conhome/10578343/proceeding 入库Ei数据库需等20-50天左右 第二届会议征稿启动&#xff08;MVIPIT 2024&#xff09; The 2nd International Conference on Machine Vision, Image Processing & Imaging Techn…

暑假提升(3)[平衡二叉树之二--红黑树]

命为志存。 —— 朱熹 红黑树RBTree 1、诞生原因2、红黑树的概念3、红黑树的性质4、红黑树的设计4、1、节点设计4、2、插入操作的设计 5、总结 1、诞生原因 由于二叉树的局限性&#xff0c;进一步出现平衡二叉树&#xff0c;来帮助我们来进一步提升我们对数据的处理&#xff0…

【LabVIEW学习篇 - 1】:初始LabVIEW

文章目录 初始LabView前面板和程序框图前面板&#xff08;Front Panel&#xff09;程序框图&#xff08;Block Diagram&#xff09;交互和工作流程 练手小案例&#xff1a;LabView中实现加法操作 初始LabView LabVIEW&#xff08;Laboratory Virtual Instrument Engineering W…

数据要素资产化路径

一、数据治理&#xff1a;包括数据规范管理、数据治理管理、元数据管理、数据架构管理。 二、数据资产运营&#xff1a;包括数据目录视图、数据全生命周期、数据资产估值、数据资产定价、数据交易流通。 方向1&#xff1a;产业数字化&#xff08;难度系数&#xff1a;*&#…