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.queryUserInfoById
        id = applyCurrentNamespace(id, false);
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);

        // 结果映射,给 MappedStatement#resultMaps
        setStatementResultMap(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 语句并正确地映射结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯物联网开发板硬件组成

第一节 开发板简介 物联网设计与开发竞赛实训平台由蓝桥杯大赛技术支持单位北京四梯科技有限公司设计和生产&#xff0c;该产品可用于参加蓝桥杯物联网设计与开发赛道的竞赛实训或院校相关课程的 实践教学环节。 开发板基于STM32WLE5无线微控制器设计&#xff0c;芯片提供了25…

常用矢量图标库

常用矢量图标库 1. iconfont 阿里巴巴旗下的矢量图标素材库&#xff1b;很强大且图标内容很丰富的矢量图标库,提供矢量图标下载&#xff08;AI / SVG / PNG / 代码格式&#xff09;、在线存储等功能&#xff0c;支持按路径改变 icon 颜色。 iconfont 网址 设备图标 2. IconP…

Unity动态读取外部图片转Texture2D,内存过大问题解决方案

问题描述 加载原始图片2.63M的图片,分辨率为3023*4032,占用内存108.5M 加载原始图片12.6 M的图片,分辨率为6000*8000,占用内存427.2M 太恐怖了吧 解决方案 1.加载完图片,等比缩放,宽高改为1024或者512以下 1024占用5.2M,512占用1.3M,相比小了很多 2.原始Texture2…

在Visual Studio 2022中配置C++计算机视觉库Opencv

本文主要介绍下载OpenCV库以及在Visual Studio 2022中配置、编译C计算机视觉库OpenCv的方法 1.Opencv库安装 ​ 首先&#xff0c;我们需要安装OpenCV库&#xff0c;作为一个开源库&#xff0c;我们可以直接在其官网下载Releases - OpenCV&#xff0c;如果官网下载过慢&#x…

【AIGC】ChatGPT 结构化 Prompt 的高级应用

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;标识符的使用&#xff08;Use of Identifiers&#xff09;1. #2. <>3. - 或 4. [] &#x1f4af;属性词的重要性和应用应用场景 &#x1f4af;具体模块…

git使用和gitlab部署

1.ci,cd,DevOps ci&#xff1a;持续集成&#xff1a;开发的代码集成到代码仓库 cd&#xff1a;持续交互&#xff1a;从代码仓库拉取代码到部署到测试环境 cd&#xff1a;持续部署&#xff1a;从代码仓库拉取代码到部署到生产环境 DevOps:开发写完的代码自动集成&#xff0c…

学习因子异步化的粒子群优化算法(AsyLnCPSO)——源码

目录 1. 学习因子异步化的概念 2. 算法步骤 2.1 初始化 2.2 迭代过程 3.优势 4. 与传统粒子群算法的区别 5.代码下载&#xff1a; 学习因子异步化的粒子群优化算法&#xff08;AsyLnCPSO&#xff09;是一种改进的粒子群优化&#xff08;PSO&#xff09;算法&#xff0c;…

BEVFormer论文总结

BEVFormer: Learning Bird’s-Eye-View Representation from Multi-Camera Images via Spatiotemporal Transformers BEVFormer&#xff1a;利用时空变换从多相机图像中学习鸟瞰表示 研究团队&#xff1a;南京大学、上海AI实验室、香港大学 ​ 代码地址&#xff1a;https://g…

ABAQUS纤维混凝土冲击破坏三维模型

纤维混凝土作为土木工程领域常用的复合材料具备良好的抗裂性及抗冲击性能&#xff0c;纤维混凝土在荷载下的破坏行为及本构关系对其应用范围具有重要影响。本案例通过AutoCAD随机三维纤维插件建立随机投放的圆柱体纤维模型&#xff0c;并将模型导入ABAQUS内&#xff0c;通过混凝…

selenium学习笔记(一)

文章目录 前言一、selenium的简介java使用seleniumPython使用selenium常用的浏览器selenium的功能 二、chromeDriver的安装查看本机的chrome版本&#xff1f;匹配对应的chromedriver并下载在服务器上例如Centos如何安装Chrome 三、selenium内容详解chrome启动chrome启动参数元素…

如何利用Java爬虫按关键字搜索苏宁易购商品

在信息技术飞速发展的今天&#xff0c;数据的获取和分析对于企业决策、市场研究和用户行为分析至关重要。本文将介绍如何使用Java编写爬虫程序&#xff0c;通过关键字搜索苏宁易购的商品&#xff0c;并获取搜索结果。 1. 爬虫简介 爬虫是一种自动化程序&#xff0c;用于从互联…

网页生成鸿蒙App

如何网页生成鸿蒙App 纯鸿蒙发布后&#xff0c;鸿蒙App需求上升。如何快速生成鸿蒙App。变色龙云(http://www.appbsl.cn)推出了鸿蒙App打包服务。可以在线自动打包鸿蒙App。 第一步 创建应用 输入网站网址&#xff0c;上传图标。 第二步 生成鸿蒙证书 打开华为开发者管理中…

【Linux进程】进程间通信(共享内存、消息队列、信号量)

目录 前言 1. System V IPC 2. 共享内存 系统调用接口 shmget ftok shmat shmdt shmctl 共享内存的读写 共享内存的描述对象 3. 消息队列 msgget msgsnd msgctl 消息队列描述对象 4. 信号量 系统调用接口 semget semctl 信号量描述对象 5. 系统层面IPC资源 6.…

endnote插参考文献【毕业论文设置】

一、GBT7714下载 https://endnote.com/downloads/styles/ 二、最新过滤器下载 Import filters - EndNote 三、设置修改 Conference Proceedings Author. Title[C]//Conference Name|. Conference Location|: Publisher|, Year of Conference|: Pages|. Thesis Author. Ti…

Temu怎么入驻:全面解析入驻流程

Temu是拼多多旗下的跨境电商平台&#xff0c;随着海外市场的不断拓展&#xff0c;该平台也吸引了许多商家的关注。一些新手商家想要入驻Temu来销售自己的产品。那Temu怎么入驻&#xff1f;下面将带来详细的入驻流程。 一、Temu入驻流程 1、注册Temu账户 首先&#xff0c;登录…

aioice里面candidate固定UDP端口测试

环境&#xff1a; aioice0.9.0 问题描述&#xff1a; aioice里面candidate固定UDP端口测试 解决方案&#xff1a; /miniconda3/envs/nerfstream/lib/python3.10/site-packages/aioice import hashlib import ipaddress import random from typing import Optional import…

【电商搜索】文档的信息论生成聚类

【电商搜索】文档的信息论生成聚类 目录 文章目录 【电商搜索】文档的信息论生成聚类目录文章信息概览研究背景技术挑战如何破局技术应用主要相关工作与参考文献后续优化方向 后记 文章信息 https://arxiv.org/pdf/2412.13534 概览 本文提出了一种基于信息论的生成聚类&#…

车载网关性能 --- GW ECU报文(message)处理机制的技术解析

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

FPGA(一)verilog语句基础

Verilog 是一种硬件描述语言&#xff08;HDL&#xff09;&#xff0c;常用于数字电路的设计、模拟和验证&#xff0c;特别是用于 FPGA 和 ASIC 的设计。Verilog 让设计者能够描述和模拟硬件系统的行为和结构&#xff0c;最终将其转化为硬件电路。 一、模块结构 Verilog 中的设计…

故障诊断 | 信号降噪算法合集

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 基于FFT CNN - BiGRU-Attention 时域、频域特征注意力融合的轴承故障识别模型-CSDN博客 基于FFT CNN - Transformer 时域、频域特征融合的轴承故障识别模型-CSDN博客 P…