MyBatis通过注解配置执行SQL语句原理源码分析
文章目录
- 前置准备
- 流程简要分析
- 配置文件解析
- 加载 Mapper 接口
- MapperAnnotationBuilder解析接口方法注解
- parseStatement 方法详解
- MapperBuilderAssistant
前置准备
创建一个mybatis-config.xml文件,配置mapper接口
<mappers><!--注解配置--><mapper class="mybatis.test.dao.IUserDao"/></mappers>
创建一个mapper接口,使用注解方式编写SQL
public interface IUserDao {@Select("SELECT id, userId, userName, userHead\n" +"FROM user\n" +"where id = #{id}")User queryUserInfoById(Long id);@Select("SELECT id, userId, userName, userHead\n" +" FROM user\n" +" where id = #{id}")User queryUserInfo(User req);@Select("SELECT id, userId, userName, userHead\n" +"FROM user")List<User> queryUserInfoList();@Update("UPDATE user\n" +"SET userName = #{userName}\n" +"WHERE id = #{id}")int updateUserInfo(User req);@Insert("INSERT INTO user\n" +"(userId, userName, userHead, createTime, updateTime)\n" +"VALUES (#{userId}, #{userName}, #{userHead}, now(), now())")void insertUserInfo(User req);@Insert("DELETE FROM user WHERE userId = #{userId}")int deleteUserInfoByUserId(String userId);}
流程简要分析
【1】 解析 mybatis-config.xml 配置文件
MyBatis 启动时加载 mybatis-config.xml 配置文件,初始化全局配置。
【2】通过配置的mapper路径反射 加载 Mapper 接口
MapperRegistry 注册 Mapper 接口类,检查每个 Mapper 是否已注册。
【3】 创建 MapperProxyFactory
为每个 Mapper 接口创建 MapperProxyFactory,用于生成接口的代理对象。
【4】 解析接口方法注解
MapperAnnotationBuilder 解析接口方法上的 SQL 注解(如 @Select、@Insert 等)。
【5】 注册 SQL 映射
通过 MappedStatement 将 SQL 语句与方法绑定,并注册到 Configuration 中。
【6】 生成代理对象
MapperProxyFactory 使用代理模式为 Mapper 接口生成代理对象。
【7】 执行 SQL 语句
代理对象拦截方法调用,解析注解中的 SQL,交由 MyBatis 执行。
配置文件解析
/*** 解析配置;类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器** @return Configuration*/public Configuration parse() {try {// 环境environmentsElement(root.element("environments"));// 解析映射器mapperElement(root.element("mappers"));} catch (Exception e) {throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}return configuration;}
mapperElement(root.element(“mappers”));代码解析出配置文件中配置的节点,获取节点对象。
遍历获取的节点,在这里会根据mapper属性中的resource和class来判断是加载xml还是接口配置SQL。如果resource不为空说明这里关联的一个外部文件,则执行xml文件的解析和SQL解析。如果是class,则进行接口注解配置方式的解析。然后通过Resources.classForName(mapperClass)加载类对象。此时就获取到了关联的接口类型。
加载 Mapper 接口
public <T> void addMapper(Class<T> type) {/* Mapper 必须是接口才会注册 */if (type.isInterface()) {if (hasMapper(type)) {// 如果重复添加了,报错throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");}// 注册映射器代理工厂knownMappers.put(type, new MapperProxyFactory<>(type));// 解析注解类语句配置MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();}}
这个方法用于将 Mapper 接口注册到 MyBatis 中。它的目的是确保 Mapper 接口可以通过注解配置 SQL 语句,并且为该接口创建代理对象。
首先检查传入的 type 是否为接口。只有接口才能作为 Mapper 接口在 MyBatis 中注册。因为 MyBatis 使用 Java 的动态代理来为接口生成实现类,如果是类(而非接口),则不能注册。hasMapper(type) 检查该 Mapper 接口是否已经被注册过。如果已经注册,抛出 RuntimeException 异常,避免重复注册同一个接口。这样做是为了防止配置冲突或逻辑错误。
通过 MapperProxyFactory 为指定的 type 接口类型创建代理工厂,并将其存储到 knownMappers 中,knownMappers是一个 Map<Class, MapperProxyFactory> 类型的集合,记录了所有已注册的 Mapper 类型及其对应的代理工厂。
MapperProxyFactory 是 MyBatis 的一个代理工厂类,它负责生成指定 Mapper 接口的代理对象。通过动态代理,MyBatis 可以拦截接口方法调用,并执行相应的 SQL 操作。
MapperAnnotationBuilder 负责解析 Mapper 接口中的注解配置(如 @Select、@Insert 等),并将注解中的 SQL 语句解析并注册到 MyBatis 的 MappedStatement 中。
MapperAnnotationBuilder解析接口方法注解
mybatis.builder.annotation.MapperAnnotationBuilder#parse
public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {assistant.setCurrentNamespace(type.getName());Method[] methods = type.getMethods();for (Method method : methods) {if (!method.isBridge()) {// 解析语句parseStatement(method);}}}}
parse() 方法的功能是遍历 Mapper 接口中的每个方法,解析其中的 SQL 注解,并将对应的 SQL 语句注册到 MyBatis 的配置中,使得接口方法能够直接执行对应的 SQL 语句。
通过 type.getMethods() 获取当前 Mapper 接口的所有方法(包括继承自 Object 类的方法)。
遍历每个方法,使用 method.isBridge() 排除桥接方法(泛型方法的代理方法)。
对每个非桥接方法,调用 parseStatement(method) 来解析该方法上的 SQL 注解,提取出 SQL 语句,并进行注册。桥接方法是 Java 编译器为支持泛型而生成的特殊方法,通常与实际的业务逻辑无关,所以在解析时被跳过。
parseStatement 方法详解
/*** 解析语句*/private void parseStatement(Method method) {Class<?> parameterTypeClass = getParameterType(method);LanguageDriver languageDriver = getLanguageDriver(method);SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);if (sqlSource != null) {final String mappedStatementId = type.getName() + "." + method.getName();SqlCommandType sqlCommandType = getSqlCommandType(method);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;String resultMapId = null;if (isSelect) {resultMapId = parseResultMap(method);}}}
parseStatement 方法是 MyBatis 中 MapperAnnotationBuilder 类的一个重要方法,用于解析 Mapper 接口中的 SQL 注解(如 @Select、@Insert 等),并根据注解创建 SqlSource 和相关的 SQL 配置(MappedStatement)。
通过 getParameterType(method) 获取当前方法的参数类型。这是为了确定 SQL 执行时需要传入的参数类型,以便正确地构造 SQL 语句。
调用 getLanguageDriver(method) 获取与该方法对应的 LanguageDriver,它用于解析 SQL 语句,并确定如何处理注解中的 SQL。
通过 getSqlSourceFromAnnotations() 方法,根据方法上的 SQL 注解(如 @Select、@Insert)获取 SqlSource。SqlSource 是 MyBatis 中封装 SQL 语句和参数的对象。
通过 type.getName()(Mapper 接口的类名)和 method.getName()(方法名)拼接得到 MappedStatement 的唯一标识符(ID)。这个 ID 用于在 MyBatis 中唯一标识一个 SQL 语句。
通过 getSqlCommandType(method) 获取当前方法对应的 SQL 类型(如 SELECT、INSERT 等)。如果是 SELECT 类型的 SQL,isSelect 设置为 true。
如果 SQL 类型是 SELECT,则通过 parseResultMap(method) 获取查询结果的 ResultMap ID。ResultMap 用于将查询结果的字段映射到 Java 对象。
MapperBuilderAssistant
/*** 添加映射器语句*/public MappedStatement addMappedStatement(String id,SqlSource sqlSource,SqlCommandType sqlCommandType,Class<?> parameterType,String resultMap,Class<?> resultType,LanguageDriver lang) {// 给id加上namespace前缀:xxx.test.dao.IUserDao.queryUserInfoByIdid = applyCurrentNamespace(id, false);MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);// 结果映射,给 MappedStatement#resultMapssetStatementResultMap(resultMap, resultType, statementBuilder);MappedStatement statement = statementBuilder.build();// 映射语句信息,建造完存放到配置项中configuration.addMappedStatement(statement);return statement;}
addMappedStatement 方法是 MyBatis 中用于创建并注册 SQL 映射语句的核心方法之一。它接受多个参数,用于配置一个完整的 SQL 语句映射并将其添加到 MyBatis 的 Configuration 中.
使用 MappedStatement.Builder 创建一个 MappedStatement 对象。MappedStatement 是 MyBatis 用于封装 SQL 语句信息、参数类型、返回结果等数据的核心对象。
调用 setStatementResultMap 方法,为 MappedStatement 设置结果映射。resultMap 定义了 SQL 查询结果与 Java 对象之间的映射关系。
通过 statementBuilder.build() 方法构建最终的 MappedStatement 对象。此时,MappedStatement 包含了 SQL 语句源、ID、结果类型、命令类型等信息。
将构建完成的 MappedStatement 添加到 MyBatis 的全局配置(configuration)中,确保该 SQL 映射信息被 MyBatis 管理和执行。
这个方法主要用于将从注解解析的 SQL 语句及相关配置注册到 MyBatis 中,使得 MyBatis 能够执行对应的 SQL 语句并正确地映射结果。
相关文章:

MyBatis通过注解配置执行SQL语句原理源码分析
文章目录 前置准备流程简要分析配置文件解析加载 Mapper 接口MapperAnnotationBuilder解析接口方法注解parseStatement 方法详解MapperBuilderAssistant 前置准备 创建一个mybatis-config.xml文件,配置mapper接口 <mappers><!--注解配置--><mapper…...

开放词汇目标检测(Open-Vocabulary Object Detection, OVOD)综述
定义 开放词汇目标检测(Open-Vocabulary Object Detection, OVOD)是一种目标检测任务,旨在检测和识别那些未在训练集中明确标注的物体类别。传统的目标检测模型通常只能识别有限数量的预定义类别,而OVOD模型则具有识别“开放词汇…...

PHP基础
PHP代码标记 标准标记:<?php ?> PHP注释 单行:// # 多行:/* */ 两种浏览器输出文本的方式:echo 和 print echo <?php header("Content-Type:text/html;charsetutf-8"); // 输出字符串 ec…...

启用WSL后,使用ssh通道连接ubuntu
Enjoy WSL 目的 启用wsl后,使用windows自带的powershell、cmd操作linux还是不太好使。以下介绍开启ssh通道,并保证能在ssh通道下,也能正常使用wsl中的win命令行,以及正常打开gui应用。 离线更新WSL,请跳转链接:离线…...

GMSSL的不同python版本
链接1(推荐) 这个使用的库,是gm ssl 3.1.1。为什么推荐?因为这个有C源码。 GitHub - GmSSL/GmSSL-Python: Python binding to the GmSSL library 链接2 这个使用的库,是gmssl 3.2.2。搜索3.2.2,找不到相…...

【数理统计】参数估计
文章目录 点估计矩估计法最大似然估计法 区间估计单个正态总体参数的区间估计均值 μ \mu μ 的区间估计方差 σ 2 \sigma^2 σ2 的区间估计 两个正态总体参数的区间估计(略)补充:单侧置信区间 点估计 矩估计法 【定义】设 X X X 是随机…...

ios 混合开发应用白屏问题
一、问题场景 项目业务中某个前端页面中使用了多个echart 组件来显示历史数据, 在反复切换到这个页面后,会出现白屏问题。 二、问题分析 0x116000ab0 - GPUProcessProxy::didClose: 0x116000ab0 - GPUProcessProxy::gpuProcessExited: reasonCrash 0x11…...

对分布式系统的理解以及redis的分布式实现
对分布式系统有哪些了解? 分布式系统是由多个独立的计算节点(通常是计算机或服务器)组成的系统,这些节点通过网络相互通信和协作,共同完成任务。分布式系统的设计旨在提供可扩展性、容错性和高可用性,适用于大规模的数据处理和服务场景。 1. 分布式系统的核心特点 分布…...

VS项目,在生成的时候自动修改版本号
demo示例:https://gitee.com/chenheze90/L28_AutoVSversion 可通过下载demo运行即可。 原理:通过csproject项目文件中的Target标签,实现在项目编译之前对项目版本号进行修改,避免手动修改; 1.基础版 效果图如下 部…...

【蓝桥杯】43699-四平方和
四平方和 题目描述 四平方和定理,又称为拉格朗日定理: 每个正整数都可以表示为至多 4 个正整数的平方和。如果把 0 包括进去,就正好可以表示为 4 个数的平方和。 比如: 502021222 712121222; 对于一个给定的正整数,可…...

我的“双胞同体”发布模式的描述与展望
当被“激情”晕染,重创标题、摘要探索“吸睛”。 (笔记模板由python脚本于2024年12月19日 15:23:44创建,本篇笔记适合喜欢编撰csdn博客的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网:https://www.python.org/ Free:大咖免…...

flask_socketio 以继承 Namespace方式实现一个网页聊天应用
点击进入上一篇,可作为参考 实验环境 python 用的是3.11.11 其他环境可以通过这种方式一键安装: pip install flask3.1.0 Flask-SocketIO5.4.1 gevent-websocket0.10.1 -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple pip list 详情如下&am…...

go mod tidy 命令
go mod tidy 是 Go 语言的命令,用于清理和更新 go.mod 和 go.sum 文件。它主要有以下功能: 移除未使用的依赖项:从 go.mod 文件中删除那些在代码中不再使用的依赖项。 添加缺失的依赖项:添加代码中使用但尚未记录在 go.mod 文件中…...

(11)YOLOv9算法基本原理
一、YOLOv9 的结构 YOLOv9 引入了可编程梯度信息(PGI),以及基于梯度路径规划的新型轻量级网络架构,为目标检测领域带来了突破性的成果。 Yolov9 网络模型主要由BackBone(主干网络)、Neck(颈层&…...

python学opencv|读取图像(十七)认识alpha通道
【1】引言 前序学习进程中,我们已经掌握了RGB和HSV图像的通道拆分和合并,获得了很多意想不到的效果,相关链接包括且不限于: python学opencv|读取图像(十二)BGR图像转HSV图像-CSDN博客 python学opencv|读…...

中小学教室多媒体电脑安全登录解决方案
中小学教室多媒体电脑面临学生随意登录的问题,主要涉及到设备使用、网络安全、教学秩序等多个方面。以下是对这一问题的详细分析: 一、设备使用问题 1. 设备损坏风险 学生随意登录可能导致多媒体电脑设备过度使用,增加设备损坏的风险。不当…...

Redis篇之Redis高可用模式参数调优,提高Redis性能
1. Redis高可用模式核心 Redis高可用模式的核心是使用主从复制和自动故障转移机制来确保系统在某些节点发生故障时仍然可以正常工作。 常用的高可用架构包括Redis Sentinel模式和Redis Cluster模式,其中Sentinel模式是为了提供高可用性而专门设计的解决方案。 在Re…...

linux-----进程execl簇函数
execl函数族概述 在Linux中,execl函数族用于在一个进程中加载并执行一个新的程序,它会替换当前进程的地址空间(代码段、数据段、堆和栈等)。这个函数族包括execl、execlp、execle、execv、execvp和execvpe,它们的主要功…...

Vue + ECharts 实现山东地图展示与交互
这篇文章中,我将逐步介绍如何使用 Vue 和 ECharts 实现一个互动式的地图展示组件,其中支持返回上一层地图、点击查看不同城市的详细信息,以及根据数据动态展示不同的统计信息。 效果图:玩转山东地图:用Echarts打造交互…...

【Verilog】UDP用户原语
User-defined primitives 概述基本语法组合逻辑的UDP时序逻辑的UDPUDP 符号表 Verilog HDL(简称 Verilog )是一种硬件描述语言,用于数字电路的系统设计。可对算法级、门级、开关级等多种抽象设计层次进行建模。 Verilog 不仅定义了语法&…...

问题小记-达梦数据库报错“字符串转换出错”处理
最近遇到一个达梦数据库报错“-6111: 字符串转换出错”的问题,这个问题主要是涉及到一条sql语句的执行,在此分享下这个报错的处理过程。 问题表现为:一样的表结构和数据,执行相同的SQL,在Oracle数据库中执行正常&…...

MyBatis入门的详细应用实例
目录 MyBatis第一章:代理Dao方式的CRUD操作1. 代理Dao方式的增删改查 第二章:MyBatis参数详解1. parameterType2. resultType 第三章:SqlMapConfig.xml配置文件1. 定义properties标签的方式管理数据库的信息2. 类型别名定义 MyBatis 第一章&…...

Sequelize ORM sql 语句工具
Sequelize ORM sql 语句工具 初始化配置 Sequelize orm 配置文章落日沉溺于海 在命令行中全局安装 npm i -g sequelize-clisequelize 执行需要匹配 mysql2 对应的依赖(安装 mysql2) npm i sequelize mysql2初始化项目 sequelize init熟悉初始化项目后…...

增强LabVIEW与PLC通信稳定性
在工业自动化系统中,上位机与PLC之间的通信稳定性至关重要,尤其是在数据采集和控制任务的实时性要求较高的场景中。LabVIEW作为常用的上位机开发平台,通过合理优化通信协议、硬件接口、数据传输方式以及系统容错机制,可以大大提升…...

UDP系统控制器_音量控制、电脑关机、文件打开、PPT演示、任务栏自动隐藏
UDP系统控制器(ShuiYX) 帮助文档 概述 本程序设计用于通过UDP协议接收指令来远程控制计算机的音量、执行特定命令和其他功能。为了确保程序正常工作,请确认防火墙和网络设置允许UDP通信,并且程序启动后会最小化到托盘图标。 命令格式及说明 音量控制…...

NK细胞杀伐功能如何实现?
在人体的免疫系统中,自然杀伐细胞(Natural Killer Cells,简称NK细胞)是一类完全自然的免疫激活力量。它们为人体提供了快速反应能力,不依赖类元的特定识别力,但能直接寻找和毁灭毒病感染细胞和肿瘤细胞。那…...

Ubuntu搭建ES8集群+加密通讯+https访问
目录 写在前面 一、前期准备 1. 创建用户和用户组 2. 修改limits.conf文件 3. 关闭操作系统swap功能 4. 调整mmap上限 二、安装ES 1.下载ES 2.配置集群间安全访问证书密钥 3.配置elasticsearch.yml 4.修改jvm.options 5.启动ES服务 6.修改密码 7.启用外部ht…...

PC寄存器(Program Counter Register)jvm
在JVM(Java虚拟机)中,PC寄存器(Program Counter Register)扮演着至关重要的角色。以下是对JVM中PC寄存器的详细解释: 一、定义与功能 定义: JVM中的PC寄存器,也被称为程序计数器,是对物理PC寄存器的一种抽象模拟。它用于存储当前线程所执行的字节码指令的地址,即指…...

预览和下载 (pc和微信小程序)
1.微信小程序 预览pdf 或者 图片等 //utils.js 文件//通过接口返回文件链接 打开文档 export default function previewFile({ downLinkUrl, tempFilePath }) {let url "https://" downLinkUrl.replace("http://", "").replace("https:…...

使用 DeepSpeed 微调 OPT 基础语言模型
文章目录 OPT 基础语言模型Using OPT with DeepSpeedmain.py 解析1、导入库和模块2、解析命令行参数3、main 函数3.1 设备与分布式初始化3.2 模型与数据准备3.3 定义评估函数3.4 优化器与学习率调度器设置3.5 使用 deepspeed 进行模型等初始化3.6 训练循环3.7 模型保存 4、dsch…...