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

MyBatis 的“魔法”:Mapper 接口是如何找到并执行 SQL 的?

图片

每一位使用 MyBatis 的 Java 开发者,都曾体验过这种“魔法”:我们只定义了一个简单的 UserMapper 接口,没有写任何实现类,但只要在 Service 中注入它并调用其方法,数据库操作就奇迹般地完成了。

// 在 Service 中
@Autowired
private UserMapper userMapper;
public User getUser(Long id) {
// 只是调用了一个接口方法
return userMapper.selectById(id);
}

UserMapper 只是一个接口,它没有实现类,那 userMapper 这个注入的 Bean 到底是什么?selectById 这个方法又是如何与具体的 SQL 语句关联起来的呢?

本文将为你揭开 MyBatis 这层神秘的面纱,深入剖析其接口与 SQL 的映射原理,并总结在 Spring Boot 环境下的最佳实践和常见陷阱。

1. 核心原理:JDK 动态代理 (Dynamic Proxy)

MyBatis 的“魔法”核心,正是 Java 的 JDK 动态代理 技术。

当你试图从 Spring 容器中获取一个 UserMapper 实例时,Spring 实际上是请求 MyBatis 的 MapperFactoryBean 来创建一个 Bean。MyBatis 并不会去寻找这个接口的实现类,而是利用 JDK 的 Proxy 类,在运行时动态地为你创建一个该接口的代理对象

这个代理对象,就是你注入到 Service 中的 userMapper。它具备了 UserMapper 接口的所有方法,但它不是一个普通的实现。你可以把它想象成一个聪明的“中介”或“调度员”。

当你调用 userMapper.selectById(1L) 时,实际发生了以下事情:

  1. 1. 方法拦截: 这个调用首先被代理对象拦截。

  2. 2. 信息解析: 代理对象会解析出你调用的信息,包括:

    • • 接口名: com.example.mapper.UserMapper

    • • 方法名: selectById

    • • 参数: 1L

  3. 3. SQL 寻址: 这是最关键的一步。代理对象会根据**“接口名 + 方法名”**这个唯一的坐标,去 MyBatis 的全局配置中寻找与之对应的 SQL 语句。

  4. 4. SQL 执行: 找到 SQL 后,代理对象会利用底层的 SqlSession,将参数 1L 传递进去,通过 JDBC 执行这条 SQL。

  5. 5. 结果映射: 将数据库返回的结果,根据配置映射成 Java 对象并返回。

所以,整个问题的关键就变成了:MyBatis 是如何完成第3步——**“SQL 寻址”**的?

2. “寻址”的艺术:两种主要的映射方式

MyBatis 提供了两种方式,来告诉代理对象“接口方法”和“SQL语句”之间的对应关系。

方式一:XML 映射文件 (最强大、最常用)

这是 MyBatis 最传统也是功能最丰富的映射方式。它遵循两条黄金法则:

法则1:namespace 必须是 Mapper 接口的全限定名。
这是连接 XML 文件和 Java 接口的“红线”。

法则2:select/insert/update/delete 等标签的 id 必须与接口中的方法名完全一致。
这是定位到具体 SQL 语句的“门牌号”。

示例:

UserMapper.java (接口定义)

package com.example.mapper;public interface UserMapper {User selectById(Long id);void insert(User user);
}

UserMapper.xml (SQL 定义)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper"><select id="selectById" resultType="com.example.model.User">SELECT * FROM users WHERE id = #{id}</select><insert id="insert">INSERT INTO users(username, email) VALUES(#{username}, #{email})</insert>
</mapper>

当调用 UserMapper.selectById() 时,代理对象会精确地找到 namespace 为 com.example.mapper.UserMapper 的 XML 文件中,id 为 selectById 的 <select> 标签,并执行其中的 SQL。

方式二:注解 (简单 SQL 的便捷之选)

对于简单的 SQL 语句,我们可以完全抛弃 XML,直接在接口方法上使用注解。

示例:

package com.example.mapper;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User selectById(Long id);@Insert("INSERT INTO users(username, email) VALUES(#{username}, #{email})")void insert(User user);
}

这种方式非常直观,但当 SQL 变得复杂,特别是需要动态 SQL(ifforeach等)时,XML 的表现力远胜于注解。

3. 让 MyBatis 找到你的 Mapper:扫描与注册

我们已经定义好了映射关系,但还需要告诉 MyBatis 去哪里“发现”这些接口和 XML 文件。

在现代 Spring Boot 项目中,这通常通过 mybatis-spring-boot-starter 自动完成,我们只需提供少量配置。

1. @MapperScan 注解 (推荐)
在你的 Spring Boot 主启动类上,添加 @MapperScan 注解,并指定 Mapper 接口所在的包路径。

@SpringBootApplication
@MapperScan("com.example.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

这个注解会告诉 MyBatis:“请扫描 com.example.mapper 包下的所有接口,并将它们注册为 Mapper Bean。”

2. @Mapper 注解
你也可以不在启动类上使用 @MapperScan,而是在每个 Mapper 接口上单独添加 @Mapper 注解。

@Mapper
public interface UserMapper { ... }

这样,只要 UserMapper 接口在 Spring Boot 的组件扫描路径下,它就会被自动发现。但当 Mapper 接口很多时,@MapperScan 显然更方便。

3. XML 文件的位置
默认情况下,MyBatis Spring Boot Starter 会在 classpath 中,寻找与 Mapper 接口同包同名的 XML 文件。

标准的 Maven 项目结构:

src/main/java/com/example/mapper/UserMapper.javaresources/com/example/mapper/UserMapper.xml  <-- XML 放在 resources 目录下,但包结构与 Java 接口保持一致

如果你想把 XML 文件集中存放在另一个地方,可以在 application.yml 中通过 mybatis.mapper-locations 属性来指定:

mybatis:mapper-locations: classpath:mappers/*.xml

4. 常见问题与陷阱

  • • BindingException: Type ... is not known to the MapperRegistry.

    • • 含义: “未在 Mapper 注册表中找到该类型”。

    • • 原因: MyBatis 根本没发现你的 Mapper 接口。请检查 @MapperScan 的包路径是否正确,或者接口上是否漏了 @Mapper 注解。

  • • BindingException: Invalid bound statement (not found): ...

    • • 含义: “无效的绑定语句(未找到)”。

    • • 原因: 接口找到了,但接口里的方法没找到对应的 SQL。请检查:

      1. 1. XML 的 namespace 是否是接口的全限定名,一个字母都不能错。

      2. 2. XML 标签的 id 是否与方法名完全一致

      3. 3. XML 文件是否被构建工具(Maven/Gradle)正确地打包进了最终的 jar 文件中。

  • • XML 文件未被打包:
    如果你的 XML 文件放在 src/main/java 目录下,Maven 默认不会打包 .xml 文件。你需要在 pom.xml 中添加如下配置:
    <build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource></resources>
    </build>

总结

MyBatis 的接口映射机制,看似“神奇”,实则建立在一套清晰、严谨的规则之上:

  1. 1. 核心是 JDK 动态代理,它在运行时为你的接口创建了一个实现类。

  2. 2. 映射的“钥匙”是“全限定接口名 + 方法名”

  3. 3. 映射关系可以通过 XML 或注解来定义。

  4. 4. @MapperScan 是告知 MyBatis 从哪里开始寻找这些“钥匙”的入口。


文章转载自:

http://yMqBNqxz.Lfqtp.cn
http://RWKJWOzw.Lfqtp.cn
http://auX2dFVo.Lfqtp.cn
http://nMZlIsSj.Lfqtp.cn
http://cLM9QZnM.Lfqtp.cn
http://RyJ6EkQs.Lfqtp.cn
http://pmutgykN.Lfqtp.cn
http://C9zM1tV2.Lfqtp.cn
http://2uWi1HHd.Lfqtp.cn
http://ZvicYay5.Lfqtp.cn
http://dxuf5z34.Lfqtp.cn
http://DXDHeRYb.Lfqtp.cn
http://lzYL35Ok.Lfqtp.cn
http://9h82ENCL.Lfqtp.cn
http://J0UgGrYI.Lfqtp.cn
http://1d9vGTCY.Lfqtp.cn
http://Bx2lhCrS.Lfqtp.cn
http://SgnE40qz.Lfqtp.cn
http://aQz3dXfy.Lfqtp.cn
http://ssRzF8DL.Lfqtp.cn
http://CTKrWkj5.Lfqtp.cn
http://gILMf8Uc.Lfqtp.cn
http://UQPRQsPH.Lfqtp.cn
http://xbEKmJ0D.Lfqtp.cn
http://ST36J7Fy.Lfqtp.cn
http://oFnIzVVK.Lfqtp.cn
http://Br8W6Biz.Lfqtp.cn
http://6GEsfMQ6.Lfqtp.cn
http://tEGcFZan.Lfqtp.cn
http://SXlHbley.Lfqtp.cn
http://www.dtcms.com/a/383660.html

相关文章:

  • 构建日志采集和分析平台
  • 《Unity+腾讯云TRTC故障排查指南:从日志盲区到线程死锁的全链路解析》
  • 笔记25.9.14(QueryWrapper,Builder ,Stream流处理,forEach)
  • 深入理解MySQL主从架构中的Seconds_Behind_Master指标
  • systemverilog如何解决不能使用变量索引来进行位选择的范围指定
  • 多语言编码Agent解决方案(1)-项目概述与架构
  • 【深度学习踩坑实录】从 Checkpoint 报错到 TrainingArguments 精通:QNLI 任务微调全流程复盘
  • 【愚公系列】《人工智能70年》019-语音识别的历史性突破(铲平技术高门槛)
  • webpack 配置文件中 mode 有哪些模式?
  • AI推理范式:从CoT到ReAct再到ToT的进化之路
  • webpack和Module Federation区别分析
  • Knockout.js Virtual Elements 详解
  • 【JavaSE五天速通|第三篇】常用API与日期类篇
  • JavaWeb-Session和ServletContext
  • HTML 编码规范
  • 深度学习(九):逻辑回归
  • 【LeetCode 每日一题】36. 有效的数独
  • 单表查询要点概述
  • 【Trans2025】计算机视觉|即插即用|WSC:即插即用!WSC模块,高光谱图像分类新SOTA!
  • Java面试小册(3)
  • 微服务项目测试接口一次成功一次失败解决办法
  • GPIO 之 EMIO 按键控制 LED 实验
  • centos安装 GNOME 桌面环境
  • 高并发投票功能设计
  • (B2B/工业/医疗行业)GEO优化服务商有哪些?哪家好?供应商推荐
  • unordered_map使用MFC的CString作为键值遇到C2056和C2064错误
  • MFC_Install_Create
  • 大数据知识框架思维导图(构造知识学习框架)
  • Spring Boot 集成第三方 API 时,常见的超时与重试机制设计
  • 设计模式——创建型模式