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.mzph.cn/diannao/43016.shtml

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

相关文章

我“硬刚”mmkv开源库对于版本号的定义赢啦!

我“硬刚”mmkv开源库胜利啦&#xff01; 前情是这个帖子https://blog.csdn.net/jzlhll123/article/details/139917169 之前项目中将mmkv1.3.4升级到1.3.5或者1.3.6&#xff0c;就从firebase后台上看到crash。 java.lang.UnsatisfiedLinkError: dlopen failed: library “libmm…

申请EV代码签名证书费用是多少?

代码签名证书是确保软件安全性和可信度的关键工具&#xff0c;在软件开发领域扮演着至关重要的角色。EV代码签名证书&#xff0c;即扩展验证代码签名证书&#xff0c;以其最高级别的安全性和信任度&#xff0c;成为大型企业或对安全性要求较高的软件的首选。本文旨在深入探讨EV…

2024最新版若依-RuoYi-Vue3-PostgreSQL前后端分离项目部署手册教程

项目简介: RuoYi-Vue3-PostgreSQL 是一个基于 RuoYi-Vue3 框架并集成 PostgreSQL 数据库的项目。该项目提供了一套高效的前后端分离的开发解决方案&#xff0c;适用于中小型企业快速构建现代化的企业级应用。此项目结合了 RuoYi-Vue-Postgresql 和 RuoYi-Vue3 的优点&#xff0…

07.C2W2.Part-of-Speech (POS) Tagging and Hidden Markov Models

往期文章请点这里 目录 OverviewPart of Speech TaggingMarkov ChainsMarkov Chains and POS TagsPOS tags as StatesTransition probabilitiesThe transition matrixInitial probabilities Hidden Markov ModelsEmission probabilitiesSummary Calculating ProbabilitiesTran…

全志A527 T527 设置左右分屏修改为单屏幕,应用分屏改为单屏

1.前言 android13中,A527的系统设置变成,左边是一级菜单,右侧是二级菜单, 这样跟我们以前android7/8/9的布局是不一样的,我们需要将它修改为一级菜单,点进去才是二级菜单这种。 效果如下 2.系统设置实现分析 它这里使用的是google新出的embedding activity, 相关的知…

LabVIEW中自定义Ring控件的图标

在LabVIEW中&#xff0c;自定义Ring控件的图标可以让用户界面更加直观和友好。以下是如何在LabVIEW中自定义Ring控件的图标的详细步骤&#xff1a; 步骤1&#xff1a;创建或获取图标 首先&#xff0c;你需要创建或获取你想要在Ring控件中使用的图标。你可以使用图像编辑软件&…

Docker拉取失败,利用github将镜像推送到阿里云

背景 由于近期国内docker镜像地址失效&#xff08;2024年6月份开始&#xff09;&#xff0c;导致pull docker 镜像总是超时。 涉及到的网址和工具 https://github.com/tech-shrimp/docker_image_pusherhttps://hub.docker.com/search阿里云 GITHUB配置 fork https://githu…

【C++初阶】与C相比,C++多出来简单又好用的语法(命名空间、输入输出、缺省参数、函数重载)

文章目录 一、 C的第一个代码1、C兼容C语言2、hello world 如何用C语法来写 二、命名空间namespace1、为什么有命名空间2、定义3、命名空间的使用4、注意事项 三、输入输出四、缺省参数1、定义2、注意 五、函数重载1、定义2、使用 六、谢谢观看&#xff01; 一、 C的第一个代码…

go mod 依赖管理补充2

依赖包的版本问题&#xff0c;别的开发语言有没有类似的问题&#xff1f;是怎么解决的&#xff1f; 举例&#xff1a;java java的依赖包的版本问题&#xff0c;通过Maven模块来操作&#xff0c;可以指定依赖包版本号&#xff0c;如下&#xff1a; go.mod 文件 go.mod文件是G…

怎么用 matlab 设计滞后-超前串联校正网络

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

Kaggle网站免费算力使用,深度学习模型训练

声明&#xff1a; 本文主要内容为&#xff1a;kaggle网站数据集上传&#xff0c;训练模型下载、模型部署、提交后台运行等教程。 1、账号注册 此步骤本文略过&#xff0c;如有需要可以参考其他文章。 2、上传资源 不论是上传训练好的模型进行预测&#xff0c;还是训练用的…

如何提升美国Facebook直播的整体体验?

Facebook作为全球最大的社交媒体平台之一&#xff0c;提供了直播功能&#xff0c;用户可以实时分享生活、见解和创意。许多商家通过美国Facebook直播来获取更多客户&#xff0c;但直播时可能会遇到网络卡顿的问题&#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;

C++规范

一、VS工具集列表&#xff1a; Visual Studio 2008&#xff1a;v90 Visual Studio 2010&#xff1a;v100 Visual Studio 2012&#xff1a;v110 Visual Studio 2013&#xff1a;v120 Visual Studio 2015&#xff1a;v140 &#xff08;v140_xp&#xff09; Visual Studio 2017&a…

FinClip SDK 入驻鸿蒙生态伙伴市场,激发鸿蒙应用创新活力

华为近期宣布开放“鸿蒙生态伙伴SDK市场”&#xff0c;甄选各类优质、安全的SDK加入聚合平台&#xff0c;致力于帮助各行业开发者轻松、高效地打造鸿蒙原生应用。 目前&#xff0c;已有18个领域、超过350个SDK适配HarmonyOS NEXT版本&#xff0c;并有超过120个SDK完成上架发布…

const 修饰不同内容区分

1.修饰局部变量 const int a 1;int const a 1; 这两种是一样的 注意&#xff1a; const int b; 该情况下编译器会报错&#xff1a;常量变量"b”需要初始值设定项 将一个变量没有赋初始值直接const修饰后&#xff0c;在以后时无法更改内容的。 2.修饰常量字符串 a.…

C++ Linux调试(无IDE)

跨平台IDE编译调试C很方便&#xff0c;如QTCreate 、VSCode、Eclipse等&#xff0c;但是如果只能使用Shell控制台呢&#xff0c;gdb调试的优势就很明显了&#xff0c;在没有IDE的情况下&#xff0c;这个方式最有效。因为上手不是很难&#xff0c;特此整理 参考链接 目录 1、G…

光伏项目开发合作模式

光伏项目开发合作模式多种多样&#xff0c;根据应用场景的不同&#xff0c;主要分为户用光伏项目合作模式和工商业光伏项目合作模式。本文将分别介绍这两种模式的特点和常见形式&#xff0c;帮助大家更好地了解光伏项目开发的市场运作。 一、户用光伏项目合作模式 1.用户全款购…

笔记本电脑内存不够

笔记本电脑内存不够是众多笔记本用户面临的常见问题&#xff0c;尤其是对于一些需要处理大型文件或者运行复杂软件的用户&#xff0c;这个问题可能会严重影响笔记本的使用体验。那么&#xff0c;我们应该如何解决笔记本电脑内存不够的问题呢&#xff1f;本文将从几个方面进行详…

基于B/S架构+java语言+ 开发工具Idea,vscode医院产科信息管理系统源码 如何标准化对接技术及各个模块

基于B/S架构java语言 开发工具Idea,vscode医院产科信息管理系统源码 如何标准化对接技术及各个模块 医院产科信息管理系统&#xff1a;标准化对接是一个涉及多个方面的复杂过程&#xff0c;旨在通过数字化手段提升产科管理的效率与质量&#xff0c;实现孕产妇全周期的健康管理…