当前位置: 首页 > news >正文

【MyBatis源码】MapperRegistry详解

🎮 作者主页:点击
🎁 完整专栏和代码:点击
🏡 博客主页:点击

文章目录

  • MapperRegistry 的作用
  • 核心字段解析
  • 整体工作流程
  • addMapper方法
  • MapperAnnotationBuilder#parse流程详解
  • MapperAnnotationBuilder#parseStatement
      • 处理主键生成策略(KeyGenerator)
      • 处理缓存、超时、ResultMap 等配置
      • 处理 ResultMap
      • 注册 MappedStatement

MapperRegistry 的作用

MapperRegistry 是 MyBatis 核心组件之一,主要负责管理 Mapper 接口的注册和获取。当通过 sqlSession.getMapper(XXXMapper.class) 获取 Mapper 实例时,会通过 MapperRegistry 返回相应的 Mapper 代理对象。

核心字段解析

private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

• Configuration:MyBatis 的全局配置对象,管理 MyBatis 的各种配置信息。
• knownMappers:用于存储已经注册的 Mapper 接口及其对应的 MapperProxyFactory。

整体工作流程

  1. Mapper 注册:
    ◦ 通过 addMapper 方法注册 Mapper 接口,将其与 MapperProxyFactory 绑定,并解析注解或 XML 配置文件。
  2. Mapper 获取:
    ◦ 通过 getMapper 方法获取 Mapper 实例,实际返回的是一个动态代理对象 (MapperProxy)。
  3. 动态代理执行:
    ◦ 当调用 Mapper 方法时,代理对象 (MapperProxy) 会拦截方法调用,并根据方法上的注解或 XML 配置执行对应的 SQL 语句。

addMapper方法

MapperRegistry#addMapper 是 MyBatis 框架中的一个核心方法,用于将 Mapper 接口注册到 MyBatis 环境中。这一过程确保 MyBatis 能够正确识别和管理 Mapper 接口,以便在实际使用时生成相应的代理对象。

public <T> void addMapper(Class<T> type) {// 判断是否是接口,如果不是接口,直接返回if (type.isInterface()) {if (hasMapper(type)) {// 防止重复注册,如果已经注册过,则抛出异常throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 添加到 knownMappers(一个 Map<Class<?>, MapperProxyFactory<?>>)中knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.// 检查该接口是否有与之关联的 XML 映射文件MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

MapperAnnotationBuilder#parse流程详解

MapperRegistry#addMapper方法流程最后是将解析流程交给了MapperAnnotationBuilder#parse,下面针对这个方法进行详细分析。
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse 是 MyBatis 中用来解析 @Mapper 接口中注解的方法,它的作用是扫描并处理 @Select, @Insert, @Update, @Delete 等 SQL 操作注解,生成对应的 MappedStatement 并将其注册到 Configuration 中。

  public void parse() {String resource = type.toString();// 检查资源是否已加载if (!configuration.isResourceLoaded(resource)) {// 解析加载mapper关联的XML文件信息,如果是注解形式则为空loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());// 解析缓存和缓存引用parseCache();parseCacheRef();// 获取当前 Mapper 接口中的所有方法for (Method method : type.getMethods()) {// 调用 canHaveStatement(method) 方法检查该方法是否适合生成 MappedStatementif (!canHaveStatement(method)) {continue;}// 处理Select和SelectProvider注解方法if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()&& method.getAnnotation(ResultMap.class) == null) {parseResultMap(method);}try {// 解析 Mapper 方法上的注解并生成 MappedStatementparseStatement(method);} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}

• 通过 configuration.isResourceLoaded(resource) 检查当前 Mapper 是否已经被加载过,如果加载过则直接返回,避免重复加载。如果没有加载过,则将其标记为已加载。
• loadXmlResource用于加载 XML 资源(即 Mapper.xml 文件),并确保其与 @Mapper 注解的接口绑定在一起。如果 Mapper.xml 存在,则将其加载到 Configuration 中,并将其中的 select, insert, update, delete 等语句转换为 MappedStatement。
• assistant.setCurrentNamespace(type.getName()) 的作用是将当前 Mapper 接口的全限定类名(如 com.example.mapper.UserMapper)设置为命名空间。MyBatis 中的命名空间是用于隔离不同 Mapper 的关键机制,确保每个 MappedStatement 的 id 是唯一的,避免冲突。
• canHaveStatement(Method method) 方法用于判断当前 Mapper 接口中的某个方法是否可以解析为 MyBatis 的 MappedStatement。这意味着只有符合条件的方法,MyBatis 才会尝试为其生成对应的 SQL 映射语句(MappedStatement)。method.isBridge()排除桥接方法。method.isDefault() 用于检查该方法是否为 Java 8 引入的接口默认方法。

MapperAnnotationBuilder#parseStatement

这段代码是 org.apache.ibatis.builder.annotation.MapperAnnotationBuilder 类的一部分,其作用是从 Mapper接口的方法(通常标注有 @Select、@Insert、@Update 或 @Delete 注解的方法)中提取信息,并构建出 MappedStatement 对象,用于 SQL 的执行。
以下是方法的总体思路:

  1. 解析方法参数类型。
  2. 获取方法所使用的语言驱动(LanguageDriver)。
  3. 从注解中构建 SqlSource 和 SqlCommandType。
  4. 解析主键生成策略。
  5. 解析缓存、超时、结果集等配置。
  6. 生成并注册 MappedStatement。
final Class<?> parameterTypeClass = getParameterType(method);
final LanguageDriver languageDriver = getLanguageDriver(method);

• getParameterType(method):获取方法的参数类型。如果方法有多个参数,则 MyBatis 会将其封装为 Map 类型。
• getLanguageDriver(method):确定使用哪种语言驱动,默认是 XMLLanguageDriver。语言驱动用于解析 SQL 语句,可以是注解中的 SQL,也可以是 XML 配置中的 SQL。

getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
  private SqlSource buildSqlSource(Annotation annotation, Class<?> parameterType, LanguageDriver languageDriver,Method method) {if (annotation instanceof Select) {return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Update) {return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Insert) {return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof Delete) {return buildSqlSourceFromStrings(((Delete) annotation).value(), parameterType, languageDriver);} else if (annotation instanceof SelectKey) {return buildSqlSourceFromStrings(((SelectKey) annotation).statement(), parameterType, languageDriver);}return new ProviderSqlSource(assistant.getConfiguration(), annotation, type, method);}

• getAnnotationWrapper():获取方法上的 SQL 注解(如 @Select、@Insert 等)。
• buildSqlSource():根据注解内容生成 SqlSource 对象,SqlSource 用于封装 SQL 语句,并处理占位符参数(如 #{})。
• getSqlCommandType():确定 SQL 命令类型,如 SELECT、INSERT、UPDATE、DELETE。

      final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options)x.getAnnotation()).orElse(null);

它的作用是从 Mapper 方法中获取 @Options 注解,并将其解析为 Options 对象,以便在后续配置 MappedStatement时使用。MyBatis 提供了 @Options 注解,用于配置 Mapper 方法的一些额外选项。常见的配置选项包括:useCache:是否启用二级缓存。timeout:SQL 执行超时时间。

处理主键生成策略(KeyGenerator)

// 处理主键生成策略(KeyGenerator)final KeyGenerator keyGenerator;String keyProperty = null;String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// 如果方法上有 @SelectKey 注解,使用其生成主键SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey)x.getAnnotation()).orElse(null);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {// 默认使用 JDBC3 的主键生成方式keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}

• 如果 SqlCommandType 是 INSERT 或 UPDATE,则可能需要生成主键。
• 优先使用 @SelectKey 注解来生成主键;如果没有,则根据配置选择是否自动生成主键。
• keyProperty 和 keyColumn 用于指定主键映射。

处理缓存、超时、ResultMap 等配置

  // 处理缓存、超时、ResultMap 等配置Integer fetchSize = null;Integer timeout = null;// statementType:语句类型,默认为 PREPARED,还可以是 STATEMENT 或 CALLABLE。StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = configuration.getDefaultResultSetType();boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;if (options != null) {// 从方法上的 @Options 注解中提取配置,如 flushCache、useCache、fetchSize 和 timeout。if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();if (options.resultSetType() != ResultSetType.DEFAULT) {resultSetType = options.resultSetType();}}

处理 ResultMap

 // 处理 ResultMapString resultMapId = null;if (isSelect) {// 如果方法是 SELECT 查询,则可能需要 ResultMap 来映射结果集到 Java 对象。ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {// 优先使用 @ResultMap 注解;如果没有,则自动生成 ResultMap。resultMapId = String.join(",", resultMapAnnotation.value());} else {resultMapId = generateResultMapName(method);}}

注册 MappedStatement

 // 注册 MappedStatementassistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,statementAnnotation.getDatabaseId(),languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);

• 调用 assistant.addMappedStatement() 将解析好的信息封装成 MappedStatement 并注册到 MyBatis 的 Configuration 中。
• MappedStatement 包含了执行 SQL 所需的所有信息,包括 SQL 语句、参数类型、结果集映射、缓存策略等。

相关文章:

【MyBatis源码】MapperRegistry详解

&#x1f3ae; 作者主页&#xff1a;点击 &#x1f381; 完整专栏和代码&#xff1a;点击 &#x1f3e1; 博客主页&#xff1a;点击 文章目录 MapperRegistry 的作用核心字段解析整体工作流程addMapper方法MapperAnnotationBuilder#parse流程详解MapperAnnotationBuilder#parse…...

Java项目实战II基于Spring Boot的工作流程管理系统设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在当今快节奏的商业环境中&#xff0c;…...

flink StreamGraph 构造flink任务

文章目录 背景主要步骤代码 背景 通常使用flink 提供的高级算子来编写flink 任务&#xff0c;对底层不是很了解&#xff0c;尤其是如何生成作业图的细节 下面通过构造一个有向无环图&#xff0c;来实际看一下 主要步骤 1.增加source 2.增加operator 3. 增加一条边&#xff0…...

【51单片机】LCD1602液晶显示屏

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 LCD1602存储结构时序结构 编码 —— 显示字符、数字 LCD1602 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是…...

理解 HTML5 Canvas 中逻辑像素与物理像素的关系

理解 HTML5 Canvas 中逻辑像素与物理像素的关系 在使用 HTML5 Canvas 时&#xff0c;开发者经常会遇到一个困惑&#xff1a;为什么鼠标的 offsetX 和 offsetY 和我绘制的图形坐标对不上&#xff1f;这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这…...

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析

揭秘C语言输入输出内幕&#xff1a;printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾&#xff1a; VS 2022 社区版C语言的安装教程&#xff0c;不要再卡在下载0B/s啦C语言入门&#xff1a;解锁基础概念&#xff0c;动手实现首个C程序C语言概念之旅&#xff1a;解锁关…...

数据分析-系统认识数据分析

目录 数据分析的全貌 观测 实验 应用 数据分析的全貌 观测 实验 应用...

蓝桥杯介绍

赛事背景与历程 自2009年举办以来&#xff0c;蓝桥杯已经连续举行了多届&#xff0c;成为国内领先的信息技术赛事。2022年&#xff0c;蓝桥杯被教育部确定为2022—2025学年面向中小学生的全国性竞赛活动&#xff0c;并入选国家级A类学科竞赛。 参赛对象与组别 蓝桥杯的参赛对…...

鸿蒙加载网络图片并转换成PixelMap

鸿蒙加载网络图片并转换成PixelMap 参考文档 基于API12. 有一些图片功能需要使用 PixelMap 类型的参数&#xff0c;但是使用Image组件之类的时候无法获取到 PixelMap 类型数据。 因此只能是把图片下载下来然后加在并转换一下。 实现方式 一下封装了一个函数。使用的 rcp 模…...

hive搭建

1.准备环境 三台节点主机已安装hadoopmysql数据库 2.环境 2.1修改三台节点上hadoop的core-site.xml <!-- 配置 HDFS 允许代理任何主机和组 --> <property><name>hadoop.proxyuser.hadoop.hosts</name><value>*</value> </property&…...

51c扩散模型~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/11541675 #Diffusion Forcing 无限生成视频&#xff0c;还能规划决策&#xff0c;扩散强制整合下一token预测与全序列扩散 当前&#xff0c;采用下一 token 预测范式的自回归大型语言模型已经风靡全球&#xff0c;同时互联…...

从零开始深度学习:全连接层、损失函数与梯度下降的详尽指南

引言 在深度学习的领域&#xff0c;全连接层、损失函数与梯度下降是三块重要的基石。如果你正在踏上深度学习的旅程&#xff0c;理解它们是迈向成功的第一步。这篇文章将从概念到代码、从基础到进阶&#xff0c;详细剖析这三个主题&#xff0c;帮助你从小白成长为能够解决实际…...

Liebherr利勃海尔 EDI 需求分析

Liebherr 使用 EDI 技术来提高业务流程的效率、降低错误率、加快数据交换速度&#xff0c;并优化与供应商、客户和其他合作伙伴之间的业务沟通。通过 EDI&#xff0c;Liebherr 实现了与全球交易伙伴的自动化数据交换&#xff0c;提升了供应链管理和订单处理的透明度。 Liebher…...

java小练习

小练1.用while语句计算11/2!1/3!1/4!...1/20!的和 public class test_11_17_2 {public static void main(String[] args) {double sum 0;double item 1;int n 20;int i 1;while(i<n){sum item;i i1;item item*(1.0/i);}System.out.println(sum);} } 小练2.计算88888…...

go语言中的占位符有哪些

在Go语言中&#xff0c;占位符主要用于格式化字符串输出&#xff0c;特别是在使用fmt包中的Printf系列函数时。以下是Go语言中常用的占位符&#xff1a; %v&#xff1a;代表值的默认格式&#xff0c;对于字符串是直接输出&#xff0c;对于整型是十进制形式。%v&#xff1a;扩展…...

基于Windows安装opus python库

项目中需要用到一些opus格式的编解码功能&#xff0c;找到网上有opus的开源库。网址&#xff1a;Opus Codec 想着人生苦短&#xff0c;没想到遇上了错误&#xff01;在这里记录一下过程 过程 安装python库 pip3 install opuslib验证 >>> import opuslib Tracebac…...

【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入

《设计模式之行为型模式》系列&#xff0c;共包含以下文章&#xff1a; 行为型模式&#xff08;一&#xff09;&#xff1a;模板方法模式、观察者模式行为型模式&#xff08;二&#xff09;&#xff1a;策略模式、命令模式行为型模式&#xff08;三&#xff09;&#xff1a;责…...

使用nossl模式连接MySQL数据库详解

使用nossl模式连接MySQL数据库详解 摘要一、引言二、nossl模式概述2.1 SSL与nossl模式的区别2.2 选择nossl模式的场景三、在nossl模式下连接MySQL数据库3.1 准备工作3.2 C++代码示例3.3 代码详解3.3.1 初始化MySQL连接对象3.3.2 连接到MySQL数据库3.3.3 执行查询操作3.3.4 处理…...

【MySQL】ubantu 系统 MySQL的安装与免密码登录的配置

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;MySQL初阶探索&#xff1a;构建数据库基础 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f4da;mysql的安装&#x1f4d5;MySQL的登录&#x1f30f;MySQL配置免密码登录 &#x1f4da;mysql的…...

高级 SQL 技巧讲解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; SQL&#xff08;结构化查询语言&#xff09;是管理和操作数据库的核心工具。从基本的查询语句到复杂的数据处理&#xff0c;掌握高级 SQL 技巧不仅能显著提高数据分析的效率&#xff0c;还能解决业务中的复…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...