乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 MyBatis概述与快速上手

    • 本章定位
    • MyBatis简介
    • 环境搭建
    • 第一个MyBatis程序
    • SqlSessionFactoryBuilder与openSession重载
    • SqlSessionFactory与SqlSession
    • SqlSession核心方法
    • 不使用 XML 构建 SqlSessionFactory
    • Mapper接口与映射方式
    • Java API 目录结构
  • 第2章 全局配置文件详解

    • 本章定位
    • properties
    • settings
    • typeAliases
    • typeHandlers
    • objectFactory
    • plugins
    • environments
    • transactionManager
    • dataSource
    • databaseIdProvider
    • mappers
    • 日志配置
  • 第3章 SQL映射文件基础

    • 本章定位
    • select
    • insert
    • update
    • delete
    • 参数传递与占位符
    • 主键生成策略
    • resultType
    • resultMap
    • 自动映射详解
    • sql片段
    • SQL 语句构建器
  • 第4章 动态SQL

    • 本章定位
    • if
    • choose、when、otherwise
    • where
    • set
    • foreach
    • trim
    • bind
    • script 元素:在注解映射器中启用动态 SQL
    • _databaseId 与动态 SQL 的多数据库支持
    • 动态 SQL 中插入脚本语言
  • 第5章 结果映射与关联查询

    • 本章定位
    • resultMap详解
    • association
    • collection
    • discriminator
    • N+1查询问题
    • 延迟加载
  • 第6章 MyBatis注解开发

    • 本章定位
    • @Select
    • @Insert
    • @Update
    • @Delete
    • @Param
    • @Options
    • @SelectKey
    • @Results
    • @Result
    • @One
    • @Many
    • @SelectProvider
  • 第7章 缓存与性能优化

    • 本章定位
    • 一级缓存
    • 二级缓存
    • 缓存配置详解
    • 自定义缓存
    • Executor执行器类型
    • 分页插件

mappers

导学

本节学习目标:

  • 理解 mappers 是 MyBatis 全局配置与 SQL 映射文件的"纽带"
  • 掌握四种 Mapper 引入方式:resource、url、class、package
  • 能够根据项目结构选择最合适的引入方式
  • 了解 Mapper 注册与解析的完整流程,规避路径错误和命名空间不匹配等常见问题

定义

mybatis-config.xml 中配置好了数据源、事务、类型别名等全局设置后,MyBatis 还需要知道去哪里加载 SQL 映射定义。这些定义可能存在于:

  • XML 文件(传统的 Mapper.xml)
  • Java 接口类(注解式 Mapper,如 @Select、@Insert)
  • 混合模式(接口 + XML)

mappers 元素的作用就是注册所有 Mapper 映射器,让 MyBatis 在初始化阶段解析并加载其中的 SQL 语句、结果映射、缓存配置等。


适用位置与核心属性

mappers 位于 mybatis-config.xml 的最末尾,是 configuration 的最后一个子元素。

<mappers>
  <!-- 方式1:从类路径加载 XML -->
  <mapper resource="mapper/EmployeeMapper.xml"/>
  
  <!-- 方式2:从绝对 URL 加载 XML -->
  <mapper url="file:///opt/config/mapper/DepartmentMapper.xml"/>
  
  <!-- 方式3:注册接口类(注解 Mapper 或接口+XML 混合) -->
  <mapper class="com.feixiang.mapper.SalaryMapper"/>
  
  <!-- 方式4:包扫描(推荐) -->
  <package name="com.feixiang.mapper"/>
</mappers>

四种引入方式对比

方式属性适用场景注意事项
resource类路径下的相对路径传统 XML Mapper,文件放在 resources 目录下路径以 resources 为根,不加 / 开头也可
url绝对 URL配置文件放在服务器固定目录或远程地址必须是绝对路径,如 file:/// 或 http://
class接口类的全限定名纯注解 Mapper,或接口与 XML 同名同路径若使用 XML,XML 必须与接口同名且放在接口所在包的类路径下
package包路径批量注册,项目中最常用扫描包下所有接口类,自动查找同名 XML

核心原理

映射器注册与解析流程图

关键机制:

  • MapperRegistry 是 Configuration 内部的映射器注册中心,维护 Map<Class<?>, MapperProxyFactory<?>>
  • 每个 Mapper 接口会被动态代理封装为 MapperProxy,当调用接口方法时,代理对象从 MappedStatements 中查找对应的 SQL 定义
  • 使用 class 或 package 时,MyBatis 会尝试在接口所在包的类路径下查找同名 XML(如 com/feixiang/mapper/EmployeeMapper.xml),实现"接口 + XML"的混合模式

完整示例

场景说明

乐途公司员工管理系统的 Mapper 文件组织如下:

  • EmployeeMapper:接口 + XML 混合模式
  • DepartmentMapper:纯注解模式
  • SalaryMapper:接口 + XML 混合模式

要求使用 package 扫描批量注册,同时演示 resource 方式的兜底配置。

操作前的状态

项目目录结构:

src/main/java/com/feixiang/mapper/
├── EmployeeMapper.java
├── EmployeeMapper.xml
├── DepartmentMapper.java
├── SalaryMapper.java
└── SalaryMapper.xml

EmployeeMapper.java:

package com.feixiang.mapper;

import com.feixiang.entity.Employee;
import org.apache.ibatis.annotations.Param;

public interface EmployeeMapper {
    Employee selectById(@Param("id") Integer id);
    int insert(Employee employee);
}

DepartmentMapper.java(纯注解):

package com.feixiang.mapper;

import com.feixiang.entity.Department;
import org.apache.ibatis.annotations.Select;

public interface DepartmentMapper {
    @Select("SELECT department_code, department_name FROM department WHERE department_code = #{code}")
    Department selectByCode(String code);
}

完整配置代码

步骤一:确保 XML 与接口同包同路径

EmployeeMapper.xml 放在 src/main/resources/com/feixiang/mapper/ 目录下(与接口包路径一致):

<?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.feixiang.mapper.EmployeeMapper">

  <select id="selectById" resultType="employee">
    SELECT employee_id, employee_name, department_code, create_time
    FROM employee
    WHERE employee_id = #{id}
  </select>

  <insert id="insert" useGeneratedKeys="true" keyProperty="employeeId">
    INSERT INTO employee (employee_name, department_code)
    VALUES (#{employeeName}, #{departmentCode})
  </insert>

</mapper>

步骤二:配置 mappers

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

  <properties resource="db.properties"/>
  <settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
  </settings>
  <typeAliases>
    <package name="com.feixiang.entity"/>
  </typeAliases>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>

  <!-- Mapper 注册配置 -->
  <mappers>
    <!-- 方式一:resource 显式指定 XML(适合少量文件或文件路径与包路径不一致时) -->
    <!-- <mapper resource="com/feixiang/mapper/EmployeeMapper.xml"/> -->

    <!-- 方式二:url 从外部加载(适合配置文件外置) -->
    <!-- <mapper url="file:///opt/mapper/EmployeeMapper.xml"/> -->

    <!-- 方式三:class 注册单个接口(适合纯注解 Mapper) -->
    <!-- <mapper class="com.feixiang.mapper.DepartmentMapper"/> -->

    <!-- 方式四:package 扫描(强烈推荐,批量注册) -->
    <package name="com.feixiang.mapper"/>
  </mappers>

</configuration>

步骤三:Java 测试代码

public class MappersDemo {
    public static void main(String[] args) throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

        try (SqlSession session = factory.openSession()) {
            // 测试 package 扫描注册的 EmployeeMapper(接口 + XML)
            EmployeeMapper empMapper = session.getMapper(EmployeeMapper.class);
            Employee emp = empMapper.selectById(1);
            System.out.println("EmployeeMapper 查询: " + (emp != null ? emp.getEmployeeName() : "无数据"));

            // 测试 package 扫描注册的 DepartmentMapper(纯注解)
            DepartmentMapper deptMapper = session.getMapper(DepartmentMapper.class);
            Department dept = deptMapper.selectByCode("DEV001");
            System.out.println("DepartmentMapper 查询: " + (dept != null ? dept.getDepartmentName() : "无数据"));
        }
    }
}

实际效果/结果

控制台输出:

EmployeeMapper 查询: 王五
DepartmentMapper 查询: 研发部

启动日志(DEBUG 级别):

DEBUG o.a.i.b.MapperRegistry - Registering mapper class com.feixiang.mapper.EmployeeMapper
DEBUG o.a.i.b.MapperRegistry - Registering mapper class com.feixiang.mapper.DepartmentMapper
DEBUG o.a.i.b.MapperRegistry - Registering mapper class com.feixiang.mapper.SalaryMapper

效果验证:

  • package 扫描自动注册了 com.feixiang.mapper 包下的所有接口
  • EmployeeMapper 和 SalaryMapper 的同名 XML 被自动发现并解析
  • DepartmentMapper 没有 XML,仅解析接口上的 @Select 注解
  • 所有 Mapper 均可通过 session.getMapper(XxxMapper.class) 正常获取

分析

  • package 扫描是实际项目中最推荐的方式,它自动处理接口与 XML 的关联,新增 Mapper 时无需修改 mybatis-config.xml
  • 使用 class 或 package 时,XML 文件必须与接口同名且位于接口所在包的类路径下。例如接口 com.feixiang.mapper.EmployeeMapper 对应的 XML 必须是 com/feixiang/mapper/EmployeeMapper.xml
  • resource 方式的路径以 resources 目录为根,写 com/feixiang/mapper/EmployeeMapper.xml 或 /com/feixiang/mapper/EmployeeMapper.xml 均可(MyBatis 会自动处理前导 /)
  • 同一个 Mapper 接口不应通过多种方式重复注册,否则启动时会报 Mapper interface xxx is already registered

易错场景/常见误区

误区错误表现正解
resource 路径写错启动报 Could not find resource mapper/EmployeeMapper.xml路径以 resources 为根,检查文件是否编译到 target/classes 对应目录下
接口与 XML 不同名package 扫描后 XML 未加载,调用方法报 Invalid bound statement (not found)确保 XML 文件名与接口名完全一致(包括大小写),且包路径一致
XML 放在 java 目录下但未被编译Maven 项目下,.xml 文件未打包到 classes 目录在 pom.xml 中配置资源过滤,或将 XML 移到 resources 目录下保持包路径
namespace 写错启动不报错,但执行时报 Invalid bound statementmapper 标签的 namespace 必须是接口的全限定名,如 com.feixiang.mapper.EmployeeMapper
重复注册同一 Mapper启动报 Mapper interface com.feixiang.mapper.EmployeeMapper is already registered避免同时使用 package 扫描和 class/resource 显式注册同一个 Mapper
纯注解 Mapper 使用 resource 指向 XML报 Could not find resource纯注解 Mapper 应使用 class 或 package 方式注册,没有 XML 文件
url 使用相对路径找不到文件url 必须是绝对路径,如 file:///C:/config/mapper/EmployeeMapper.xml

面试考点

Q1:MyBatis 注册 Mapper 有哪几种方式?推荐使用哪种?

A:四种方式:resource(加载类路径 XML)、url(加载绝对 URL 的 XML)、class(注册接口类)、package(扫描包下所有接口)。推荐使用 package 扫描,因为它能批量注册,自动关联接口与同名 XML,新增 Mapper 时无需修改全局配置文件,维护成本最低。

Q2:使用 package 扫描时,MyBatis 如何找到接口对应的 XML 文件?

A:MyBatis 会根据接口的全限定名,将 . 替换为 /,并在类路径下查找同名 XML。例如接口 com.feixiang.mapper.EmployeeMapper 会查找 com/feixiang/mapper/EmployeeMapper.xml。因此 XML 必须与接口同名,且放在与接口包路径对应的类路径目录下。

Q3:启动时提示 Invalid bound statement (not found),可能的原因有哪些?

A:常见原因包括:① Mapper 未被注册到 mappers 中;② XML 的 namespace 与接口全限定名不匹配;③ XML 中的 id 与接口方法名不匹配;④ XML 文件未被编译到类路径(如放在 java 目录下未配置 Maven 资源过滤);⑤ 接口与 XML 不同名或路径不一致(使用 package/class 时)。

Q4:同一个 Mapper 能否同时用注解和 XML 定义 SQL?

A:可以,但不推荐。如果接口方法上同时存在注解(如 @Select)和 XML 中同 id 的语句,MyBatis 的加载顺序可能导致覆盖行为不确定。最佳实践是二选一:简单查询用注解,复杂 SQL(动态 SQL、关联映射)用 XML。


小结

mappers 是 MyBatis 全局配置的"收官之笔",它将精心编写的 SQL 映射定义注册到框架中,使其能够被业务代码调用。四种引入方式各有适用场景,但 package 扫描以其批量、自动、低维护的优势成为项目首选。记住:路径正确、namespace 匹配、避免重复注册,是 Mapper 配置的三条铁律。


下一章引子

至此,MyBatis 全局配置文件的十大核心元素已全部拆解完毕。从 properties 的配置外部化,到 settings 的行为总控,再到 mappers 的映射注册,这些元素共同构成了一套完整的 MyBatis 运行环境。在下一章中,我们将离开全局配置,深入 Mapper XML 的世界,学习 resultMap 的高级映射技巧——这是处理复杂关联查询、嵌套结果、鉴别器映射的必备技能。

上一页
databaseIdProvider
下一页
日志配置