MyBatis 源码解析:Mapper 文件加载与解析
引言
在 MyBatis 中,Mapper
文件扮演了至关重要的角色,它通过 SQL 映射文件来定义数据库查询操作和 Java 对象之间的映射关系。Mapper 文件通常是以 XML 格式存储的,包含了 SQL 语句以及与 Java 对象的对应关系。在本篇文章中,我们将通过自定义实现一个简单的 Mapper 文件解析器,展示 MyBatis 是如何加载和解析这些 SQL 映射文件的,并对其内部的工作原理进行详细剖析。
摘要
Mapper
文件是 MyBatis 中连接 SQL 和 Java 对象的桥梁。本文将通过自定义实现一个简单的 Mapper 文件加载与解析器,展示其工作原理,并对比 MyBatis 中的 Mapper 文件解析过程,帮助读者理解 MyBatis 内部的 Mapper 文件加载与解析机制。
什么是 MyBatis Mapper 文件
在 MyBatis 中,Mapper
文件是用来定义 SQL 语句和 Java 对象之间映射关系的文件。通常,一个 Mapper
文件以 XML 的格式存在,并且定义了诸如<select>
、<insert>
、<update>
、<delete>
等标签,用来映射 SQL 语句。
一个典型的 Mapper 文件示例如下:
<mapper namespace="com.example.mapper.UserMapper"><select id="getUserById" parameterType="int" resultType="User">SELECT * FROM users WHERE id = #{id}</select><insert id="insertUser" parameterType="User">INSERT INTO users (name, age) VALUES (#{name}, #{age})</insert>
</mapper>
在上面的示例中,<mapper>
标签定义了一个命名空间,<select>
和 <insert>
标签分别定义了 SQL 查询和插入语句。
手动实现 Mapper 文件加载与解析
接下来,我们将通过一个简化的自定义实现,展示如何加载和解析 MyBatis 的 Mapper 文件。
步骤概述
- 定义 Mapper 解析器:创建一个简单的 Mapper 文件加载和解析器。
- 加载 XML 文件:读取并解析 XML 文件中的 SQL 映射。
- 解析 SQL 映射:解析
<select>
和<insert>
标签中的 SQL 信息。 - 实现测试类:验证自定义 Mapper 文件解析器的工作流程。
定义 Mapper 解析器
首先,我们定义一个简单的 MapperFileParser
类,用于加载和解析 XML 格式的 Mapper 文件。
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;/*** Mapper 文件解析器,用于加载和解析 XML 格式的 SQL 映射文件*/
public class MapperFileParser {/*** 解析给定的 Mapper 文件* @param filePath Mapper 文件路径*/public void parseMapperFile(String filePath) {try {// 加载并解析 XML 文件File file = new File(filePath);DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse(file);// 获取根元素 <mapper>Element rootElement = document.getDocumentElement();String namespace = rootElement.getAttribute("namespace");System.out.println("Mapper namespace: " + namespace);// 解析 <select> 和 <insert> 等标签NodeList nodeList = rootElement.getChildNodes();for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);// 处理 <select> 标签if (node instanceof Element && "select".equals(node.getNodeName())) {Element selectElement = (Element) node;String id = selectElement.getAttribute("id");String parameterType = selectElement.getAttribute("parameterType");String resultType = selectElement.getAttribute("resultType");String sql = selectElement.getTextContent().trim();System.out.println("Select ID: " + id + ", Parameter Type: " + parameterType + ", Result Type: " + resultType);System.out.println("SQL: " + sql);}// 处理 <insert> 标签if (node instanceof Element && "insert".equals(node.getNodeName())) {Element insertElement = (Element) node;String id = insertElement.getAttribute("id");String parameterType = insertElement.getAttribute("parameterType");String sql = insertElement.getTextContent().trim();System.out.println("Insert ID: " + id + ", Parameter Type: " + parameterType);System.out.println("SQL: " + sql);}}} catch (Exception e) {e.printStackTrace();}}
}
说明:
- 我们使用了 Java 的
DocumentBuilderFactory
和DocumentBuilder
来解析 XML 文件,并获取 Mapper 文件中的 SQL 语句。 parseMapperFile()
方法负责加载 XML 文件并解析其中的<select>
和<insert>
标签。
加载 XML 文件
接下来,我们通过 MapperFileParser
类加载并解析 Mapper 文件。
public class MapperFileParserTest {public static void main(String[] args) {// 创建解析器MapperFileParser parser = new MapperFileParser();// 解析示例 Mapper 文件String filePath = "src/main/resources/UserMapper.xml"; // 指定 Mapper 文件路径parser.parseMapperFile(filePath);}
}
示例 Mapper 文件(UserMapper.xml):
<mapper namespace="com.example.mapper.UserMapper"><select id="getUserById" parameterType="int" resultType="User">SELECT * FROM users WHERE id = #{id}</select><insert id="insertUser" parameterType="User">INSERT INTO users (name, age) VALUES (#{name}, #{age})</insert>
</mapper>
测试结果
运行解析器后,输出结果如下:
Mapper namespace: com.example.mapper.UserMapper
Select ID: getUserById, Parameter Type: int, Result Type: User
SQL: SELECT * FROM users WHERE id = #{id}
Insert ID: insertUser, Parameter Type: User
SQL: INSERT INTO users (name, age) VALUES (#{name}, #{age})
我们成功解析了 Mapper 文件中的命名空间、SQL 语句、以及其参数类型和结果类型。
类图与流程图
为了更好地理解 Mapper 文件的加载与解析过程,我们提供了类图和流程图。
类图
流程图
MyBatis中的 Mapper 文件加载与解析
在 MyBatis 中,Mapper 文件的加载和解析是通过 XMLMapperBuilder
类来完成的。XMLMapperBuilder
负责读取 Mapper 文件并将其映射为 MyBatis 的内存结构。MyBatis 使用 XPath
解析 XML 文件,处理 SQL 映射、参数类型和结果类型。
MyBatis的 XML 解析
MyBatis 使用了 XPathParser
类来解析 Mapper 文件的内容:
public class XMLMapperBuilder extends BaseBuilder {private final XPathParser parser;private final MapperBuilderAssistant builderAssistant;public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {super(configuration);this.parser = new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver());this.builderAssistant = new MapperBuilderAssistant(configuration, resource);}public void parse() {parseMapper(parser.evalNode("/mapper"));}
}
MyBatis中的 Mapper 文件结构
在 MyBatis 中,Mapper 文件的结构通常包括:
- 命名空间:标识 Mapper 文件的唯一性。
- SQL 语句:通过
<select>
、<insert>
、<update>
和<delete>
标签定义 SQL 语句。 - 参数和结果映射:指定 SQL 语句的参数类型和结果类型。
MyBatis 在解析这些 Mapper 文件时,会将 SQL 语句映射到 Java 方法上,结合动态 SQL 生成最终的 SQL 语句。
对比分析:手动实现与 MyBatis 的区别
1
. 功能复杂度:
- MyBatis:MyBatis 的 Mapper 文件解析器支持复杂的动态 SQL、嵌套查询、缓存等功能,并且能够自动管理 SQL 的参数和结果映射。
- 简化实现:我们的手动实现仅支持解析简单的 SQL 映射,没有处理动态 SQL、结果集映射和缓存等复杂特性。
-
扩展性:
- MyBatis:MyBatis 支持丰富的扩展机制,如自定义插件、拦截器等,能够灵活应对不同的业务需求。
- 简化实现:我们的实现是一个基础的解析器,主要用于展示 XML 文件解析的基本原理,缺乏高级扩展能力。
-
性能和优化:
- MyBatis:MyBatis 针对大规模应用进行了大量的性能优化,能够高效地解析和管理大量的 Mapper 文件。
- 简化实现:我们实现的版本没有进行性能优化,适用于简单的场景。
总结
通过手动实现一个简化的 Mapper 文件解析器,我们了解了 MyBatis 是如何加载和解析 XML 格式的 SQL 映射文件的。MyBatis 的 Mapper 文件解析机制非常灵活,能够将 SQL 语句与 Java 对象进行紧密的绑定,使得开发者能够以更加简洁的方式处理数据库操作。理解 Mapper 文件的加载与解析原理,将帮助您在实际项目中更好地使用 MyBatis 并优化 SQL 操作。
互动与思考
你在项目中是否遇到过需要解析 SQL 配置文件的场景?你认为 MyBatis 的 Mapper 文件机制在哪些方面最有帮助?欢迎在评论区分享你的经验与见解!
如果你觉得这篇文章对你有帮助,请别忘了:
- 点赞 ⭐
- 收藏 📁
- 关注 👀
让我们一起深入学习 MyBatis 框架,成为更优秀的开发者!
相关文章:
MyBatis 源码解析:Mapper 文件加载与解析
引言 在 MyBatis 中,Mapper 文件扮演了至关重要的角色,它通过 SQL 映射文件来定义数据库查询操作和 Java 对象之间的映射关系。Mapper 文件通常是以 XML 格式存储的,包含了 SQL 语句以及与 Java 对象的对应关系。在本篇文章中,我…...

(11)(2.1.2) DShot ESCs(二)
文章目录 前言 3 配置伺服功能 4 检查RC横幅 5 参数说明 前言 DShot 是一种数字 ESC 协议,它允许快速、高分辨率的数字通信,可以改善飞行器控制,这在多旋翼和 quadplane 应用中特别有用。 3 配置伺服功能 如上所述,如果使用…...

yolov5/8/9模型在COCO分割数据集上的应用【代码+数据集+python环境+GUI系统】
yolov5/8/9模型在COCO分割数据集上的应用【代码数据集python环境GUI系统】 yolov5/8/9模型在COCO分割数据集上的应用【代码数据集python环境GUI系统】 1.COCO数据集介绍 COCO数据集,全称为Microsoft Common Objects in Context,是微软于2014年出资标注的…...

技术周总结 09.16~09.22 周日(架构 C# 数据库)
文章目录 一、09.16 周一1.1)问题01: 软件质量属性中"质量属性场景"、"质量属性环境分析"、"质量属性效用树"、"质量属性需求用例分析"分别是什么?1.2)问题02: 软件质量属性中…...

【java实现json转化为CSV文件】
文章目录 JSON文件中的数据格式测试文件转换的接口 JSON文件中的数据格式 单条数据展开后如下: {"text": "《邪少兵王》是冰火未央写的网络小说连载于旗峰天下","spo_list":[{"predicate": "作者", "objec…...

MySQL索引知识个人笔记总结(持续整理)
本篇笔记是个人整理的索引知识总结,刚开始有点乱,后续会一直边学边整理边总结 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。就好比索引就是数据的目录 索引结构 Btree索引,Hash索引,Full-text索引,R-tree(空…...

ReKep——李飞飞团队提出的让机器人具备空间智能:基于视觉语言模型GPT-4o和关系关键点约束
前言 由于工厂、车厂的任务需求场景非常明确,加之自今年年初以来,我司在机器人这个方向的持续大力度投入(包括南京、长沙两地机器人开发团队的先后组建),使得近期我司七月接到了不少来自车厂/工厂的订单,比如其中的三个例子&…...

[Java并发编程] synchronized(含与ReentrantLock的区别)
文章目录 1. synchronized与ReentrantLock的区别2. synchronized的作用3. synchronized的使用3.1 修饰实例方法,作用于当前实例,进入同步代码前需要先获取实例的锁3.2 修饰静态方法,作用于类的Class对象,进入修饰的静态方法前需要…...

spring-boot-maven-plugin插件打包和java -jar命令执行原理
文章目录 1. Maven生命周期2. jar包结构2.1 不可执jar包结构2.2 可执行jar包结构 3. spring-boot-maven-plugin插件打包4. 执行jar原理 1. Maven生命周期 Maven的生命周期有三种: clean:清除项目构建数据,较为简单,不深入探讨&a…...

Python办公自动化教程(001):PDF内容提取
1、Pdfplumber介绍 pdfplumber的github地址: https://github.com/jsvine/pdfplumber/【介绍】:pdfplumber 是一个用于处理 PDF 文件的 Python 第三方库,它提供了一种方便的方式来提取 PDF 文件中的文本、表格和其他信息。【功能】ÿ…...

HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践
鸿蒙HarmonyOS开发实战往期文章必看: HarmonyOS NEXT应用开发性能实践总结 最新版!“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通) 非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&am…...
【AI学习】了解OpenAI o1背后的self-play RL:开启新的智能道路
在ChatGPT刚刚出来的时候,沐神关于ChatGPT有一段视频,只有几分钟,却是讲得极其透彻的一段。大概意思就是,过去的AI智能水平,比如五年前,大概相当于人类5秒钟思考的程度,包括自动驾驶,…...

Java项目实战II基于Java+Spring Boot+MySQL的车辆管理系统(开发文档+源码+数据库)
目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 "随着…...

IPsec-VPN中文解释
网络括谱图 IPSec-VPN 配置思路 1 配置IP地址 FWA:IP地址的配置 [FW1000-A]interface GigabitEthernet 1/0/0 [FW1000-A-GigabitEthernet1/0/0]ip address 10.1.1.1 24 //配置IP地址 [FW1000-A]interface GigabitEthernet 1/0/2 [FW1000-A-GigabitEthernet1/0/2]ip a…...

Ubuntu 22.04 源码下载、编译
Kernel/BuildYourOwnKernel - Ubuntu Wikihttps://wiki.ubuntu.com/Kernel/BuildYourOwnKernel 一、查询当前系统内核版本 rootubuntu22:~# uname -r 5.15.0-118-generic 二、查询本地软件包数据库中的内核源码信息 rootubuntu22:~# apt search linux-source Sorting... Do…...

【深度学习实战—11】:基于Pytorch实现谷歌QuickDraw数据集的下载、解析、格式转换、DDP分布式训练、测试
✨博客主页:王乐予🎈 ✨年轻人要:Living for the moment(活在当下)!💪 🏆推荐专栏:【图像处理】【千锤百炼Python】【深度学习】【排序算法】 目录 😺〇、仓库…...

基于SpringBoot+WebSocket实现地图上绘制车辆实时运动轨迹图
实现基于北斗卫星的车辆定位和轨迹图的Maven工程(使用模拟数据),我们将使用以下技术: Spring Boot:作为后端框架,用来提供数据接口。Thymeleaf:作为前端模板引擎,呈现网页。Leaflet…...

嵌入式入门小工程
此代码基于s3c2440 1.点灯 //led.c void init_led(void) {unsigned int t;t GPBCON;t & ~((3 << 10) | (3 << 12) | (3 << 14) | (3 << 16));t | (1 << 10) | (1 << 12) | (1 << 14) | (1 << 16);GPBCON t; }void le…...

hackmyvm靶场--zon
环境 攻击机kali 靶机 未知 主机探测 因为在同一个局域网内使用ARP协议探测存活主机 靶机为192.168.56.128 端口探测 常见的80和22端口 那么一定是寻找web漏洞拿shell了 后台扫描 后台扫描常用dirsearch和gobuster,有时候小字典可能不太行,可以尝试换个大点…...
atcoder abc372 启发式合并, dp
A delete 代码: #include <bits.stdc.h>using namespace std;int main() {string s;cin >> s;for(auto t: s) if(t ! .) cout << t; } B 3 ^ A 思路:三进制转换,可以参考二进制,先把当前可以加入的最大的3的…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...