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 语句并正确地映射结果。

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

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

相关文章

如何优雅的关闭GoWeb服务器

以下内容均为Let’s Go Further内容节选以及作者本人理解。 这里创建了一个后台进程用于捕获关闭信号&#xff0c;在后台进程中&#xff0c;主要内容为&#xff1a; 创建一个缓冲通道 quit使用signal.Notify函数监听并捕获关机信号SIGINT,SIGTERM&#xff0c;在捕获关机信号后…

入侵他人电脑,实现远程控制(待补充)

待补充 在获取他人无线网网络密码后&#xff0c;进一步的操作是实现入侵他人电脑&#xff0c;这一步需要获取对方的IP地址并需要制作自己的代码工具自动化的开启或者打开对方的远程访问权限。 1、获取IP地址&#xff08;通过伪造的网页、伪造的Windows窗口、hook&#xff0c;信…

如何构建《交易体系》

文章目录 1、看大盘定仓位1.1 严格的仓位管理策略1.2 永远不满仓,永远不空仓1.3 行情好重仓做收益,行情不好轻仓控回撤,一年抓住2~3波行情1.4 上涨行情6-8成,震荡行情5-6成,下跌行情4成以内2、精选赛道(选择大于努力)2.1 只做主线赛道只做一级题材,远离杂毛题材2.2 选对…

mac 安装graalvm

Download GraalVM 上面链接选择jdk的版本 以及系统的环境下载graalvm的tar包 解压tar包 tar -xzf graalvm-jdk-<version>_macos-<architecture>.tar.gz 移入java的文件夹目录 sudo mv graalvm-jdk-<version> /Library/Java/JavaVirtualMachines 设置环境变…

[SZ901]JTAG高速下载设置(53Mhz)

SZ901最高支持JTAG 53MHz的时钟频率&#xff0c;下载bit文件和固化程序的速度提升非常明显。 首先设置参数 1&#xff0c;将JTAG0 分频系数修改为3 2&#xff0c;设置参数&#xff0c;更新参数。&#xff08;完成&#xff09; 打开VIVADO VIVADO 正常识别FPGA&#xff0c;速…

蓝桥杯刷题——day8

蓝桥杯刷题——day8 题目一题干解题思路代码 题目二题干解题思路代码 题目一 题干 N 架飞机准备降落到某个只有一条跑道的机场。其中第i架飞机在 Ti时刻到达机场上空&#xff0c;到达时它的剩余油料还可以继续盘旋 Di个单位时间&#xff0c;即它最早可以于 Ti时刻开始降落&am…

如何解决vscode powershell乱码

如何解决vscode powershell乱码 在 Visual Studio Code 中使用 PowerShell 时出现乱码&#xff0c;通常是由于终端编码设置或字体不匹配导致的。以下是解决 PowerShell 乱码问题的步骤&#xff1a; 设置 PowerShell 的默认编码 PowerShell 默认的输出编码可能与终端编码不一…

MyBatis入门的详细应用实例

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

深度科普文:细数倾斜摄影数据的缺点

1. 引言 写这篇文章的起因是最近遇到一个使用倾斜摄影数据应标的三维可视化项目&#xff0c;业主认为倾斜摄影数据加载很卡&#xff0c;要求能浏览场景的时候能立刻显示出当前的场景最精细的模型&#xff0c;如下图1所示。其实这个问题遇到的次数还真不少&#xff0c;作为乙方…

wordpress调用指定分类ID下 相同标签的内容

要在WordPress中调用分类ID为1、3、7的分类下&#xff0c;具有相同标签的前10个内容&#xff0c;可以使用自定义的WordPress查询(WP_Query)。以下是实现此功能的步骤和示例代码&#xff1a; 步骤&#xff1a; 确定共同标签&#xff1a; 首先&#xff0c;你需要确定分类1、3、…

GESP CCF C++八级编程等级考试认证真题 2024年12月

202412 GESP CCF C八级编程等级考试认证真题 1 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 小杨家响应国家“以旧换新”政策&#xff0c;将自家的汽油车置换为新能源汽车&#xff0c;正在准备自编车牌。自编车牌包括5 位数字或英文字母&#xff0c…

React:闭包陷阱产生和解决

在 React 中&#xff0c;闭包陷阱是一个常见的问题&#xff0c;尤其是在处理异步操作、事件处理器、或是定时器时。理解闭包的工作原理以及它在 React 中如何与状态和渲染交互&#xff0c;可以帮助你避免陷入一些常见的错误。 一、闭包陷阱的产生 1、什么是闭包陷阱&#xff1…

Vue.js前端框架教程1:Vue应用启动和Vue组件

文章目录 Vue 应用Vue 应用的主要组成部分&#xff1a;启动 Vue 应用&#xff1a; Vue组件基础组件组件注册父子组件组件插槽&#xff08;Slots&#xff09;动态组件和 keep-alive Vue 应用 Vue 应用由几个主要部分组成&#xff0c;每个部分都有其特定的角色和职责。以下是 Vu…

vue-cli 5接入模块联邦 module federation

vue-cli 5接入模块联邦 module federation 模块联邦概念实现思路配置遇到的问题: 模块联邦概念 模块联邦由webpack 5最先推出的,让应用加载远程的代码模块来实现不同的Web应用共享代码片段.模块联邦分为两个角色,一个是生产者,一个是消费者.生产者暴露代码供消费者消费 (用一个…

【开源免费】基于SpringBoot+Vue.JS在线宠物用品交易网站(JAVA毕业设计)

本文项目编号 T 092 &#xff0c;文末自助获取源码 \color{red}{T092&#xff0c;文末自助获取源码} T092&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【Git 常用操作:pull push】

Git 基本概念 Git 是一个先进的开源的分布式版本控制系统&#xff0c;常用于管理工作内容、项目代码等功能。 Git 工作流程 图片来源&#xff1a;https://www.runoob.com/git/git-basic-operations.html 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff…

thinking claude从入门到精通

什么是thinking claude 简而言之就是一个提示工程,能够激发大模型类人类思考模式。 "Thinking Claude" 是一个由17岁高中生涂津豪开发的项目,旨在增强AI模型Claude-3.5的“深度思维”能力,使它的思考逻辑更加接近人类。这个工具通过引入所谓的“深度思考协议”和…

shell脚本的循环-----while和for循环

一、while 1.格式 while 条件表达式; do 命令 done 2.案例 &#xff1a; ping测试子网段的主机网段由用户输入&#xff0c;例如用户输入192.168.101 &#xff0c;则ping192.168.101.125 — 192.101.131 UP&#xff1a; /tmp/host_up.txt Down: /tmp/host_down.txt &#…

内容与资讯API优质清单

作为开发者&#xff0c;拥有一套API合集是必不可少的。这个开发者必备的API合集汇集了各种实用的API资源&#xff0c;为你的开发工作提供了强大的支持&#xff01;无论你是在构建网站、开发应用还是进行数据分析&#xff0c;这个合集都能满足你的需求。你可以通过这些免费API获…

maven-resources-production:ratel-fast: java.lang.IndexOutOfBoundsException

Maven生产环境中遇到java.lang.IndexOutOfBoundsException的问题&#xff0c;尝试了重启电脑、重启IDEA等常规方法无效&#xff0c;最终通过直接重建工程解决了问题。 Rebuild Project 再启动OK