SpringBoot中间件—ORM(Mybatis)框架实现
目录
定义
需求背景
方案设计
代码展示
UML图
实现细节
测试验证
总结
源码地址(已开源):https://gitee.com/sizhaohe/mini-mybatis.git 跟着源码及下述UML图来理解上手会更快,拒绝浮躁,沉下心来搞
定义:
ORM:Object Relational Mapping --> 对象关系映射,是一种程序设计技术,用于实现面向对象编程语言里面不同类型系统的数据之间的转换
需求背景:
记不记得刚开始学JAVA时,编写一大串JDBC相关代码来进行与数据库的交互,日后我们接触到的MyBatis、MyBatisPlus等都是使用ORM组件来实现的框架。
本篇文章提炼出mybatis【最】经典、【最】精简、【最】核心的代码设计,来实现一个【mini-mybatis】,从而熟悉并掌握ORM框架的涉及实现。
方案设计:

- 中间的四部分处理是ORM框架的核心内容
- 这个框架会提供出SqlSession工厂以及调用方式
代码展示
UML图
很重要,建议code前跟我一样,先将类UML图整理出来,整个类的依赖关系及代码执行流程会一目而然。
篇幅有限,展开观看

- 以上为ORM框架实现核心类:加载mysql配置文件、对mapper-xml解析、获取数据库session、操作数据库及封装响应结果。
实现细节
1.定义sqlsession接口
对数据库的定义和处理,本篇我们只封装一个 T selectOne(Object param);
public interface SqlSession {<T> T selectOne(String statement, Object parameter);void close();
}
2.DefaultSqlSession(SqlSession的实现)
使用rt.jar包下(java.lang.sql包下)
Connection接口(负责与数据库进行连接)及PreparedStatement(执行具体sql)接口来实现
public class DefaultSqlSession implements SqlSession{private Connection connection;private Map<String,XNode> mapperElement;public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {this.connection = connection;this.mapperElement = mapperElement;}@Overridepublic <T> T selectOne(String statement, Object parameter) {XNode xNode = mapperElement.get(statement);Map<Integer, String> parameterMap = xNode.getParameter();try {PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());buildParameter(preparedStatement, parameter, parameterMap);// SQL执行结果集的行数据ResultSet resultSet = preparedStatement.executeQuery();List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));return objects.get(0);} catch (Exception e) {e.printStackTrace();}return null;}private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {List<T> list = new ArrayList<>();try {ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();// 每次遍历行值while (resultSet.next()) {T obj = (T) clazz.newInstance();for (int i = 1; i <= columnCount; i++) {Object value = resultSet.getObject(i);String columnName = metaData.getColumnName(i);String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);Method method;if (value instanceof Timestamp) {method = clazz.getMethod(setMethod, Date.class);} else {method = clazz.getMethod(setMethod, value.getClass());}method.invoke(obj, value);}list.add(obj);}} catch (Exception e) {e.printStackTrace();}return list;}@Overridepublic void close() {if (null == connection) return;try {connection.close();} catch (SQLException e) {e.printStackTrace();}}private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map<Integer, String> parameterMap) throws SQLException, IllegalAccessException {int size = parameterMap.size();// 单个参数if (parameter instanceof Long) {for (int i = 1; i <= size; i++) {preparedStatement.setLong(i, Long.parseLong(parameter.toString()));}return;}else{// TODO 后面紧跟的章节继续补充其他类型的入参}}
}
3.定义SqlSessionFactory接口
每次执行一个SQL语句,应用程序都需要获取一个SqlSession对象。SqlSession对象是执行持久化操作的入口点,可以用于执行SQL语句、刷新缓存、提交事务等操作。建议在使用完SqlSession后,及时关闭它来释放资源。
public interface SqlSessionFactory {SqlSession openSession();
}
4.DefaultSqlSessionFactory(上述接口实现类)
构造方法中向下传递了Configuration配置文件
public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}@Overridepublic SqlSession openSession() {return new DefaultSqlSession(configuration.getConnection(), configuration.getMapperElement());}
}
5.SqlSessionFactoryBuilder
数据库操作的核心类,负责解析Mapper文件(拿datasource,数据库连接信息,mapper文件中sql的各个信息如id,入返参类型,sql)
public class SqlSessionFactoryBuilder {public DefaultSqlSessionFactory build(Reader reader) {SAXReader saxReader = new SAXReader();Document document = null;try {document = saxReader.read(new InputSource(reader));// 拿到根标签元素Element rootElement = document.getRootElement();Configuration configuration = parseConfiguration(rootElement);return new DefaultSqlSessionFactory(configuration);} catch (DocumentException e) {e.printStackTrace();}return null;}public Configuration parseConfiguration(Element rootElement) {Configuration configuration = new Configuration();configuration.setDataSource(dataSource(rootElement.selectNodes("//dataSource")));configuration.setConnection(connection(configuration.getDataSource()));configuration.setMapperElement(mapperElement(rootElement.selectNodes("//mappers")));return configuration;}private Map<String, String> dataSource(List<Element> list) {Map<String, String> dataSource = new HashMap<>(4);Element element = list.get(0);List content = element.content();for (Object o : content) {Element e = (Element) o;String name = e.attributeValue("name");String value = e.attributeValue("value");dataSource.put(name, value);}return dataSource;}private Connection connection(Map<String, String> dataSource) {try {return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));} catch (SQLException e) {e.printStackTrace();}return null;}private Map<String, XNode> mapperElement(List<Element> list) {Map<String, XNode> map = new HashMap<>();Element element = list.get(0);List content = element.content();try {for (Object o : content) {Element e = (Element) o;// 拿到mapper文件对应地址String resource = e.attributeValue("resource");Reader reader = Resources.getResourceAsReader(resource);SAXReader saxReader = new SAXReader();Document document = saxReader.read(new InputSource(reader));Element rootElement = document.getRootElement();String namespace = rootElement.attributeValue("namespace");List<Element> selectNodes = rootElement.selectNodes("select");for (Element ele : selectNodes) {String id = ele.attributeValue("id");String parameterType = ele.attributeValue("parameterType");String resultType = ele.attributeValue("resultType");String sql = ele.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, "?");}XNode xNode = new XNode();xNode.setId(id);xNode.setNameSpace(namespace);xNode.setParameterType(parameterType);xNode.setResultType(resultType);xNode.setSql(sql);xNode.setParameter(parameter);map.put(namespace + "." + id, xNode);}}}catch (Exception e){e.printStackTrace();}return map;}
}
测试验证
建表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` bigint(20) NOT NULL COMMENT '自增id',`userId` varchar(9) DEFAULT NULL COMMENT '用户ID',`userNickName` varchar(32) DEFAULT NULL COMMENT '用户昵称',`userHead` varchar(255) DEFAULT NULL COMMENT '用户头像',`userPassword` varchar(255) DEFAULT NULL COMMENT '用户密码',`createTime` datetime DEFAULT NULL COMMENT '创建时间',`updateTime` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, '001', 'xxx', '001', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
INSERT INTO `user` VALUES (2, '002', 'xxx2', '002', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
COMMIT;SET FOREIGN_KEY_CHECKS = 1;
定义POJO及DAO
@Data
public class User {private Long id;private String userId; // 用户IDprivate String userNickName; // 昵称private String userHead; // 头像private String userPassword; // 密码private Date createTime; // 创建时间private Date updateTime; // 更新时间
}
public interface IUserDao {User queryUserInfoById(Long id);
}
ORM配置文件--mybatis-config-datasource.xml
<?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><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://172.17.1.245:3306/airticketbasedb?useUnicode=true"/><property name="username" value="write"/><property name="password" value="write123"/></dataSource></environment></environments><mappers><mapper resource="mapper/User_Mapper.xml"/></mappers></configuration>
Mapper配置
UserMapper.xml
<?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.example.minimybatis.dao.IUserDao"><select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.example.minimybatis.po.User">SELECT id, userId, userNickName, userHead, userPassword, createTimeFROM userwhere id = #{id}</select></mapper>
测试类
public class ApiTest {@Testpublic void test(){String resouce = "mybatis-config-datasource.xml";Reader reader;try{reader = Resources.getResourceAsReader(resouce);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);SqlSession sqlSession = sqlSessionFactory.openSession();User user = sqlSession.selectOne("com.example.minimybatis.dao.IUserDao.queryUserInfoById",1L);System.out.println(JSONObject.toJSONString(user));}catch (Exception e){e.printStackTrace();}}
}

总结
比mybatis小很多,取其(mybaits)精华来达到掌握ORM框架的目的
相关文章:
SpringBoot中间件—ORM(Mybatis)框架实现
目录 定义 需求背景 方案设计 代码展示 UML图 实现细节 测试验证 总结 源码地址(已开源):https://gitee.com/sizhaohe/mini-mybatis.git 跟着源码及下述UML图来理解上手会更快,拒绝浮躁,沉下心来搞 定义&#x…...
结构化思维:高效能项目经理人的底层能力
大家好,我是老原。 我们经常会说「高效能」,那怎么成为高效能人士?其实除了拼体力和心力以外,高效能更重要的是脑力,这里的脑力不是指智力,而是结构化思维。 随着你在职场中不断成长和进阶,级…...
Pytorch个人学习记录总结 07
目录 神经网络-非线性激活 神经网络-线形层及其他层介绍 神经网络-非线性激活 官方文档地址:torch.nn — PyTorch 2.0 documentation 常用的:Sigmoid、ReLU、LeakyReLU等。 作用:为模型引入非线性特征,这样才能在训练过程中…...
vue3+ts+elementui-plus二次封装树形表格
复制粘贴即可: 一、定义table组件 <template><div classmain><div><el-table ref"multipleTableRef" :height"height" :default-expand-all"isExpend" :data"treeTableData"style"width: 100%…...
机器学习/深度学习常见算法实现(秋招版)
包括BN层、卷积层、池化层、交叉熵、随机梯度下降法、非极大抑制、k均值聚类等秋招常见的代码实现。 1. BN层 import numpy as npdef batch_norm(outputs, gamma, beta, epsilon1e-6, momentum0.9, running_mean0, running_var1)::param outputs: [B, L]:param gamma: mean:p…...
京东技术专家首推:Spring 微服务架构设计,GitHub 星标 128K
前言 本书提供了实现大型响应式微服务的实用方法和指导原则,并通过示例全面 讲解如何构建微服务。本书深入介绍了 Spring Boot、Spring Cloud、 Docker、Mesos 和 Marathon,还会教授如何用 Spring Boot 部署自治服务,而 无须使用重量级应用服…...
R语言--森林图制作
#数据准备- data5 #install.packages("rmda")rm(list=ls())library(MASS)library(rmda)library(dplyr) #mutate依赖环境library(magrittr) #%>%依赖setwd("D:/R/nomo5new2")data...
Tomcat中利用war包部署
在Tomcat中利用war包部署Web应用程序时,默认情况下,应用程序的上下文路径(也称为项目名称)将是war文件的名称(去除.war扩展名)。这意味着您在访问Web应用程序时必须在URL中包含项目名称。例如,如…...
[JAVAee]线程安全
目录 线程安全的理解 线程不安全的原因 ①非原子性 ②可见性 ③代码重排序 体会何为不安全的线程 保证线程安全 一个代码在多线程的环境下就很容易出现错误. 线程安全的理解 线程安全是什么呢?通俗的来讲,线程安全就是在多线程的环境下,代码的结果是符合我们预期的,就…...
ELK环境搭建——概况
Elastic Stack,核心产品包括 Elasticsearch、Kibana、Beats 和 Logstash等等。能够安全可靠地从任何来源获取任何格式的数据,然后对数据进行搜索、分析和可视化。 目录 一:Elasticsearch: 1.1 从数据中探寻各种问题的答案 1.1.1 定义您自己的搜索方式...
面试知识点整理
计算机的物理内存是有限的,所以操作系统在遇到内存不足时,会通过换页机制暂时把 某个进程未使用的内存中的数据搬移到硬盘上(比如 Linux 的 swap 分区),并在系统页表中 删除相应的表项。当该进程访问数据已经被搬移到硬…...
腾讯云服务器CVM计算型c6/c5实例CPU型号、处理器主频大全
腾讯云服务器CVM计算型C6、C5、C4、CN3、C3和C2实例,计算型C6云服务器CPU采用Intel Xeon Ice Lake处理器,主频3.2GHz,睿频3.5GHz,腾讯云服务器网分享更多计算型CVM云服务器CPU型号、处理器主频性能说明: 目录 云服务…...
vue3笔记-脚手架篇
第一章 基础篇 第二章 脚手架篇 vue2与vue3的一些区别 响应式系统: Vue 2 使用 Object.defineProperty 进行响应式数据的劫持和监听,它对数据监听是一项项的进行监听,因此,当新增属性发生变化时,它无法监测到&…...
数字的补数
题目: 对整数的二进制表示取反(0 变 1 ,1 变 0)后,再转换为十进制表示,可以得到这个整数的补数。 例如,整数 5 的二进制表示是 "101" ,取反后得到 "010" &…...
Taskfile demo
https://github.com/yangyang5214/blog/issues/1 makefile 很好用,但是有些语法我不会。 go-task yml & shell 不错,推荐 Taskfile.yml https://github.com/go-task/task/blob/main/.golangci.yml # go install github.com/go-task/task/v3/cmd/ta…...
MyBatis学习笔记之高级映射及延迟加载
文章目录 环境搭建,数据配置多对一的映射的思路逻辑级联属性映射association分布查询 一对多的映射的思路逻辑collection分布 环境搭建,数据配置 t_class表 t_stu表 多对一的映射的思路逻辑 多对一:多个学生对应一个班级 多的一方是st…...
小程序如何删除/上架/下架商品
在小程序中,产品的删除、上架和下架是常见的操作,可以根据实际需求来管理商品的展示与销售。下面将介绍如何在小程序中删除上架下架商品的具体步骤。 进入商品管理页面, 在个人中心点击管理入口,然后找到“商品管理”菜单并点击。…...
Failed to load local font resource:微信小程序加载第三方字体
加载本地字体.ttf 将ttf转换为base64格式:https://transfonter.org/ 步骤如下 将下载后的stylesheet.css 里的font-family属性名字改一下,然后引进页面里就行了,全局样式就放app.scss,单页面就引入单页面 注: .title…...
使用fastjson错误
说明:使用fastjson时,对象解析不成功,一直报错,但是json格式没有错; 错误信息:Method threw ‘com.alibaba.fastjson.JSONException’ exception. json数据是正确的 分析:注意看,fa…...
【GitOps系列】使用Kustomize和Helm定义应用配置
文章目录 使用 Kustomize 定义应用改造示例应用1.创建基准和多环境目录2.环境差异分析3.为 Base 目录创建通用 Manifest4.为开发环境目录创建差异 Manifest5.为预发布环境创建差异 Manifest6.为生产环境创建差异 Manifest 部署 Kustomize 应用部署到开发环境部署到生产环境 使用…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
