Mapper接口的绑定方式有哪几种?
本文介绍了MyBatis关联Mapper接口与SQL的四种核心绑定方式。从传统的XML手动配置(resource、class),到高效的包扫描(package、@MapperScan),对比了各自的优缺点,并强调了
@MapperScan在现代Spring项目中的最佳实践地位。
MyBatis中,将Mapper接口和它的SQL语句(无论是定义在XML中还是注解中)关联起来的过程称为“绑定”(Binding)。MyBatis框架必须知道调用哪个接口方法时,应该去执行哪条SQL。
主要有以下几种绑定方式,从传统到现代,演进得越来越方便。
核心规则:命名空间(Namespace)
在讨论具体方式之前,必须先理解一个核心概念:XML映射文件中的namespace属性必须指向Mapper接口的全限定名。这是MyBatis能够将XML与接口关联起来的根本约定。
例如,如果你的接口是:
package com.example.mapper;
public interface UserMapper {
User selectById(int id);
}
那么对应的XML文件头部必须是:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- SQL语句的id必须与接口方法名一致 -->
<select id="selectById" resultType="com.example.model.User">
select * from user where id = #{id}
</select>
</mapper>
理解了这个基础约定后,我们再来看MyBatis是如何“发现”并加载这些Mapper的。
四种主要的Mapper绑定(扫描)方式
1. 通过XML的<mapper>标签,使用resource属性(最传统)
这是最原始、最明确的方式。你需要在MyBatis的核心配置文件 mybatis-config.xml 中,手动指定每一个Mapper XML文件的路径。
配置示例 (mybatis-config.xml):
<configuration>
...
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/OrderMapper.xml"/>
<!-- 每增加一个新的Mapper,都需要在这里手动添加一行 -->
</mappers>
</configuration>
- 工作原理:MyBatis启动时,会读取这个配置文件,并根据
resource属性指定的类路径(classpath)去加载对应的XML文件。 - 优点:配置非常明确,一目了然。XML文件可以放在项目的任何位置,只要路径正确即可。
- 缺点:非常繁琐。每当新增一个Mapper,都必须修改核心配置文件,容易遗漏,维护成本高。
2. 通过XML的<mapper>标签,使用class属性
这种方式是上一种的改进,它通过指定接口的类名来寻找映射文件。
配置示例 (mybatis-config.xml):
<configuration>
...
<mappers>
<mapper class="com.example.mapper.UserMapper"/>
<mapper class="com.example.mapper.OrderMapper"/>
</mappers>
</configuration>
- 工作原理:MyBatis会根据指定的接口全限定名,去寻找与之同名且在同一包路径下的XML文件。
- 例如,对于
com.example.mapper.UserMapper,它会去类路径下寻找com/example/mapper/UserMapper.xml文件。
- 例如,对于
- 优点:比
resource方式更优,因为你不需要关心XML文件的具体物理路径,只需要保证它和接口在同一个包下即可。IDE构建工具(如Maven/Gradle)通常会默认将src/main/java下的Java文件和src/main/resources下的资源文件合并到同一个类路径下,所以这个约定很方便实现。 - 缺点:仍然需要手动在配置文件中添加每一个Mapper接口,还是很繁琐。
3. 通过XML的<package>标签,进行包扫描(推荐的非Spring方式)
这是对前两种方式的巨大改进,从“逐个注册”变成了“批量扫描”。
配置示例 (mybatis-config.xml):
<configuration>
...
<mappers>
<package name="com.example.mapper"/>
<!-- 自动扫描该包下的所有Mapper接口 -->
</mappers>
</configuration>
- 工作原理:MyBatis会自动扫描
com.example.mapper包及其子包下的所有接口,然后按照与方式二相同的规则(同名、同包)去查找并加载对应的XML映射文件。 - 优点:一劳永逸。一旦配置好包路径,后续在该包下新增任何Mapper接口,都无需再修改配置文件,MyBatis会自动发现它们。
- 缺点:要求Mapper接口和XML文件必须遵循“同包同名”的约定。
4. 基于注解扫描(Spring/Spring Boot集成下的主流方式)
在与Spring或Spring Boot集成的环境下,我们通常不再使用mybatis-config.xml来配置mappers,而是使用注解来完成。
配置示例 (Spring Boot启动类或配置类):
@SpringBootApplication
@MapperScan("com.example.mapper") // 关键注解
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
或者在每个Mapper接口上单独标记:
@Mapper
public interface UserMapper {
// ...
}
// 配合@ComponentScan,@Mapper本身是@Component
- 工作原理:
@MapperScan注解告诉MyBatis-Spring整合包去扫描指定的包,为找到的每一个接口生成代理对象,并将其注册为Spring Bean。- 如果接口上使用了SQL注解(如
@Select,@Insert等),那么SQL就直接从注解中获取,甚至不需要XML文件。 - 如果接口没有使用SQL注解,MyBatis会依然遵循与方式二相同的规则(同名、同包)去寻找并加载XML文件来获取SQL。
- 优点:
- 完全融入了Spring的生态,配置简单,符合现代Java开发习惯。
- 对于简单的SQL,可以直接写在注解里,省去XML文件,非常便捷。
- 对于复杂的动态SQL,仍然可以继续使用XML文件,兼具灵活性。
- 缺点:无明显缺点,是目前最主流、最推荐的使用方式。
总结与对比
| 绑定方式 | 配置位置 | 如何工作 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|---|
1. resource |
mybatis-config.xml |
手动指定每个XML文件的路径 | 灵活,文件位置不限 | 极其繁琐,维护成本高 | 遗留项目或特殊文件布局 |
2. class |
mybatis-config.xml |
手动指定每个接口的类名 | 无需硬编码路径 | 仍然繁琐,需要逐个添加 | 较少使用,是向包扫描的过渡 |
3. package |
mybatis-config.xml |
指定包名,自动扫描 | 方便,一次配置,自动发现 | 需遵循"同包同名"约定 | 独立使用MyBatis(无Spring) |
4. @MapperScan |
Spring配置类 | 注解指定包名,自动扫描 | 最方便,与Spring无缝集成,支持纯注解或XML混合 | 无明显缺点 | Spring/Spring Boot项目(首选) |
在现代的开发中,尤其是使用Spring Boot时,第四种方式(@MapperScan)是绝对的主流和最佳实践。它结合了注解的便利性和XML的强大功能,让开发效率和代码可维护性都达到了最佳平衡。