MyBatis Mapper 代理与 SQL 参数
MyBatis Mapper 接口为什么没有实现类?
使用 MyBatis 时,我们通常只写一个 Mapper 接口,却能直接注入并调用它。实现类在哪里?答案是 MyBatis 在运行时为 Mapper 接口创建代理对象,并把方法调用映射到对应的 SQL 配置。
1. 从接口到代理对象
1 | |
项目启动时,MyBatis 与 Spring 的整合配置会扫描 Mapper 接口,并向 Spring 容器注册相应的 Mapper 工厂对象。注入时,框架通过代理机制提供接口实例。调用 findById 后,代理根据 Mapper 命名空间、方法名和参数找到映射语句,创建并执行数据库操作,再把结果映射回 Java 对象。
常见流程可以概括为:
1 | |
注解 SQL 和 XML SQL 是两种定义映射语句的方式。简单查询用注解较直接;复杂动态 SQL、结果映射通常更适合 XML。是否扫描到接口取决于项目的 Mapper 扫描配置。
2. #{}:绑定参数的默认选择
1 | |
#{} 会把参数作为预编译语句的值绑定,SQL 结构与参数值分开处理。这通常是传递用户输入的正确方式,也能避免把普通字符串直接拼进 SQL。
3. ${}:原样替换,必须限制输入
1 | |
${} 会把字符串直接替换进 SQL 文本,常用于表名、列名等不能作为普通值绑定的位置。它不会自动把用户输入变安全,因此不能把请求参数未经检查地传给 ${}。
若排序字段需要动态选择,应使用白名单映射:
1 | |
随后只把白名单中的列名交给 SQL,并继续使用绑定参数传递筛选值。
4. 动态 SQL 的常见结构
MyBatis XML 提供 <if>、<where>、<set> 和 <foreach> 等标签,用于按条件生成 SQL。比如可选筛选条件用 <if>,更新字段用 <set>,批量 ID 查询用 <foreach>。动态 SQL 仍要检查空集合、空字符串和字段权限,避免生成无效或过宽的语句。
5. 排查 Mapper 调用
- Mapper 是否被扫描并注册到 Spring 容器?
- XML 的 namespace、
id是否与接口全限定名和方法名对应? - 参数名是否能被映射到 SQL 中?多参数时是否显式使用
@Param? - 返回列与 Java 属性是否匹配,是否需要
resultMap或驼峰映射? - 最终生成的 SQL 和绑定参数是否符合预期?
6. 小结
Mapper 不是“凭空有了实现”,而是运行时代理把接口方法与 SQL 映射连接起来。传值优先用 #{};确实需要替换 SQL 标识符时,用严格白名单控制 ${} 的输入。



