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();//解析rootElementSystem.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 和 valueif (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();@Overridepublic <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数据封装到对象 -- monsterMonster 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,deleteprivate 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方法@Overridepublic 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的functionsList<Function> functions = mapperBean.getFunctions();//判断当前的mapperBean 解析对应的MapperXML后,有方法if (null != functions || 0 != functions.size()){for (Function function : functions) {//当前要执行的方法和function.getFunctionNameif (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);
}

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

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

相关文章

【Java探索之旅】多态:向上下转型、多态优缺点、构造函数陷阱

文章目录 &#x1f4d1;前言一、向上转型和向下转型1.1 向上转型1.2 向下转型 二、多态的优缺点2.1 多态优点2.2 多态缺陷 三、避免避免构造方法中调用重写的方法四、好的习惯&#x1f324;️全篇总结 &#x1f4d1;前言 在面向对象编程中&#xff0c;向上转型和向下转型是常用…

Docker-11☆ Docker Compose部署RuoYi-Cloud

一、环境准备 1.安装Docker 附:Docker-02-01☆ Docker在线下载安装与配置(linux) 2.安装Docker Compose 附:Docker-10☆ Docker Compose 二、源码下载 若依官网:RuoYi 若依官方网站 鼠标放到"源码地址"上,点击"RuoYi-Cloud 微服务版"。 跳转至G…

Splunk Enterprise 任意文件读取漏洞(CVE-2024-36991)

文章目录 前言漏洞描述影响版本漏洞复现POC批量检测-nuclei脚本 修复建议 前言 Splunk Enterprise 是一款强大的机器数据管理和分析平台&#xff0c;能够实时收集、索引、搜索、分析和可视化来自各种数据源的日志和数据&#xff0c;帮助企业提升运营效率、增强安全性和优化业务…

C++11右值引用及移动构造

区分左值和右值 在学习c11的右值引用前&#xff0c;大家肯定会有点陌生什么是右值&#xff1f;什么是左值&#xff1f;现在我先来带大家熟悉一下概念。 左值 可以被取地址&#xff0c;也可被修改&#xff08;const修饰的除外&#xff09; 可以出现在等号左边&#xff0c;也可…

软件架构之开发方法

软件架构之开发方法 第6章&#xff1a;开发方法6.1 软件生命周期6.2 软件开发模型6.2.1 瀑布模型6.2.2 演化模型6.2.3 螺旋模型6.2.4 增量模型6.2.5 构件组装模型 6.3 统一过程6.4 敏捷方法6.4.1 极限编程6.4.2 特征驱动开发6.4.3 Scrum6.4.4 水晶方法6.4.5 其他敏捷方法 6.5 软…

开源模型应用落地-FastAPI-助力模型交互-进阶篇(一)

一、前言 FastAPI 的高级用法可以为开发人员带来许多好处。它能帮助实现更复杂的路由逻辑和参数处理&#xff0c;使应用程序能够处理各种不同的请求场景&#xff0c;提高应用程序的灵活性和可扩展性。 在数据验证和转换方面&#xff0c;高级用法提供了更精细和准确的控制&#…

Java线程的创建·启动和休眠

一.线程的创建和启动 Java中创建线程的两种方式 ◆继承java.lang.Thread类 ◆实现java.lang.Runnable接口 ◆使用线程的步骤 继承Thread类创建线程 ◆自定义线程类继承自Thread类 ◆重写run()方法&#xff0c;编写线程执行体 ◆创建线程对象&#xff0c;调用start()方法启动…

数据驱动制造业升级,免费可视化工具成关键

制造业作为国民经济的支柱产业&#xff0c;正经历着前所未有的变革。数据&#xff0c;作为这场变革的核心驱动力&#xff0c;其重要性不言而喻。然而&#xff0c;面对海量且复杂的数据&#xff0c;如何高效、直观地将其转化为有价值的洞察&#xff0c;成为了众多制造企业亟待解…

前端面试题25(css常用的预处理器)

在前端开发领域&#xff0c;CSS预处理器在面试中经常被提及&#xff0c;其中最流行的三种预处理器是Sass、LESS和Stylus。下面分别介绍它们的特点和优势&#xff1a; 1. Sass&#xff08;Syntactically Awesome Style Sheets&#xff09; 优势&#xff1a; 变量&#xff1a;允…

实战Qt开发WordBN笔记软件#01 搭建开发环境:VS2019+Qt6.5+CMake+Git

01 背景 【WordBN字远笔记】是天恩软件工作室开发的一款免费笔记软件&#xff1b;WordBN基于VS2019、Qt6.5开发&#xff0c;使用Qt Quick&#xff08;QML&#xff09;开发语言。 本课程将以【WordBN字远笔记】的界面为实战基础&#xff0c;详细介绍如何基于Qt/QML开发语言&am…

从新手到高手:Scala函数式编程完全指南,Scala IF…ELSE 语句(8)

1、Scala IF…ELSE 语句 Scala IF…ELSE 语句是通过一条或多条语句的执行结果&#xff08;True或者False&#xff09;来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: 1.1、if 语句 if 语句有布尔表达式及之后的语句块组成。 语法 if 语句的语法格式如下&…

LT7911UX 国产原装 一拖三 edp 转LVDS 可旋转 可缩放

2.一般说明 该LT7911UX是一种高性能Type-C/DP1.4a到MIPI或LVDS芯片的VR/显示应用。HDCP RX作为HDCP转发器的上游&#xff0c;可以与其他芯片的HDCP TX配合实现转发器功能。 对于DP1.4a输入&#xff0c;LT7911UX可配置为1/2/4通道。自适应均衡使其适用于长电缆应用&#xff0c;最…

python对象

类 我们目前所学习的对象都是Python内置的对象但是内置对象并不能满足所有的需求&#xff0c;所以我们在开发中经常需要自定义一些对象类&#xff0c;简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象类就是对象的图纸&#xff01;我们也称对象是类的实例&#…

给您介绍工控CAN总线

CAN是什么 CAN&#xff0c;全称Controller Area Network&#xff0c;即控制器局域网&#xff0c;是一种由Bosch公司在1983年开发的通信协议。它主要用于汽车和工业环境中的电子设备之间的通信。CAN协议定义了物理层和数据链路层的通信机制&#xff0c;使得不同的设备能够通过CA…

LabVIEW开发商业软件的多角度分析与注意事项

在使用LabVIEW开发商业软件时&#xff0c;有许多方面需要考虑和注意&#xff0c;包括项目管理、架构设计、性能优化、用户体验、安全性、维护与支持等。以下是从多个角度详细分析在LabVIEW中开发商业软件需要注意的事项。 项目管理 需求分析&#xff1a;确保深入了解客户需求&a…

BFS:边权相同的最短路问题

一、边权相同最短路问题简介 二、迷宫中离入口最近的出口 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:const int dx[4]{1,-1,0,0};const int dy[4]{0,0,1,-1};int nearestExit(vector<vector<char>>& maze, vector<int>& e…

使用C++实现ATM系统,谈谈思路及代码实现

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码&#xff1a; 新建主图幅图类型指标都可以&#xff01; VAR1:(HL)/2; 唇:REF(SMA(VAR1,5,1),3),COLORGREEN; 齿:REF(SMA(VAR1,8,1),5),COLORRED; 颚:REF(SMA(VAR1,13,1),8),COLORBLUE;

Unity实现安卓App预览图片、Pdf文件和视频的一种解决方案

一、问题背景 最近在开发app项目&#xff0c;其中有个需求就是需要在app软件内显示图片、pdf和视频&#xff0c;一开始想的解决方案是分开实现&#xff0c;也就是用Image组件显示图片&#xff0c;找一个加载pdf的插件和播放视频的插件&#xff0c;转念一想觉得太麻烦了&#x…

张量分解(2)——张量运算(内积、外积、直积、范数)

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…