0%

mybatis全局配置文件

MyBatis 全局配置文件深度解析:从核心节点到工程实践

MyBatis 全局配置文件(默认命名为 mybatis-config.xml)是 MyBatis 框架的 “总开关”,用于配置数据库连接、映射规则、缓存策略、插件等核心参数。其配置节点有严格的顺序要求(必须按 properties→settings→typeAliases→typeHandlers→objectFactory→plugins→environments→databaseIdProvider→mappers 排列,乱序会导致解析报错),每个节点都承担特定职责。本文将逐一拆解每个节点的核心作用、配置方式、实际应用场景及工程最佳实践,帮助开发者掌握 MyBatis 配置的精髓。

根节点 <configuration>:配置入口

  • 核心作用:作为所有配置节点的父容器,定义 MyBatis 的全局配置上下文。

  • 关键规则:子节点必须按固定顺序排列(MyBatis 解析时会严格校验顺序,顺序错误会抛出 ConfigurationException)。

  • 示例结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?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/>
    <settings/>
    <typeAliases/>
    <typeHandlers/>
    <objectFactory/>
    <plugins/>
    <environments/>
    <databaseIdProvider/>
    <mappers/>
    </configuration>

<properties>:属性配置(外部化配置)

核心作用

将数据库连接信息(如 URL、用户名、密码)、环境变量等外部化,避免硬编码在配置文件中,支持动态替换(如开发 / 测试 / 生产环境切换)。

三种配置方式与优先级

MyBatis 加载属性的优先级从低到高如下(高优先级覆盖低优先级):

  1. 子节点 <property>:直接在 <properties> 内定义的属性;
  2. 外部配置文件:通过 resource(类路径)或 url(网络 / 磁盘路径)引入的 .properties 文件;
  3. 代码传参:构建 SqlSessionFactory 时通过 Properties 对象传入的参数。
(1)方式 1:子节点 <property> 配置
1
2
3
4
5
<properties>
<!-- 直接定义属性,优先级最低 -->
<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.username" value="root"/>
</properties>
(2)方式 2:引入外部配置文件(推荐)
  • 外部文件src/main/resources/jdbc.properties

    1
    2
    jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
    jdbc.password=123456
  • MyBatis 配置引用:

    1
    2
    3
    4
    <properties resource="jdbc.properties">
    <!-- 子节点属性会被外部文件覆盖(若同名) -->
    <property name="jdbc.username" value="dev_root"/> <!-- 最终生效为外部文件的 root -->
    </properties>
(3)方式 3:代码传参(优先级最高)
1
2
3
4
5
// 构建 SqlSessionFactory 时传入属性,覆盖外部配置
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
Properties props = new Properties();
props.put("jdbc.password", "prod_123456"); // 生产环境密码,覆盖外部文件的 123456
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is, props);

3. 高级特性:默认值与分隔符

通过配置属性启用默认值特性,避免因属性缺失导致启动失败:

1
2
3
4
5
6
7
8
9
10
11
<properties resource="jdbc.properties">
<!-- 1. 启用默认值特性(默认关闭) -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
<!-- 2. 自定义默认值分隔符(默认是 ":",可改为 "?:" 避免与 URL 中的 ":" 冲突) -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
</properties>

<!-- 使用默认值:若 jdbc.password 未配置,默认使用 123456 -->
<dataSource type="POOLED">
<property name="password" value="${jdbc.password?:123456}"/>
</dataSource>

4. 工程实践

  • Spring Boot 整合:无需单独的jdbc.properties,直接引用application.yml中的配置:

    1
    2
    3
    4
    5
    6
    7
    # application.yml
    spring:
    datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

    MyBatis 可通过${spring.datasource.url}直接引用 Spring 配置的属性。

<settings>:核心功能开关(重中之重)

<settings> 是 MyBatis 最核心的配置节点,用于覆盖 MyBatis 的默认行为,控制缓存、延迟加载、驼峰映射等关键功能。以下筛选最常用且影响最大的配置项详细说明:

配置名 核心作用 有效值 默认值 工程建议
cacheEnabled 全局开启 / 关闭二级缓存(一级缓存默认开启,不受此配置影响) true/false true 生产环境建议开启,提升查询性能;高频写场景可关闭
lazyLoadingEnabled 全局开启延迟加载(关联对象(如 User 的 Orders)仅在访问时加载) true/false false 关联查询多的场景(如订单查用户)建议开启,减少冗余加载
aggressiveLazyLoading 开启后,访问任意属性会加载所有延迟关联;关闭后仅按需加载(3.4.1+ 默认 false) true/false false(3.4.1+) 始终关闭,避免加载不必要的关联数据
mapUnderscoreToCamelCase 自动将数据库下划线命名(如 user_name)映射为 Java 驼峰命名(userName true/false false 强制开启,减少 resultMap 的配置量
logImpl 指定日志实现(打印 SQL 语句,便于调试) SLF4J/LOG4J/STDOUT_LOGGING 未设置 开发环境用 STDOUT_LOGGING(控制台打印),生产用 SLF4J(结合 Logback)
defaultExecutorType 配置默认执行器(SIMPLE:普通;REUSE:复用 PreparedStatement;BATCH:批量) SIMPLE/REUSE/BATCH SIMPLE 批量插入 / 更新场景用 BATCH,提升性能;普通场景用 SIMPLE
useGeneratedKeys 允许 JDBC 自动生成主键(支持 MySQL 自增、Oracle 序列) true/false false 强制开启,配合 keyProperty 获取自增主键(如插入后返回用户 ID)

配置示例(生产环境常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭激进延迟加载,按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启驼峰自动映射(user_name → userName) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 控制台打印 SQL(开发环境) -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 允许自动生成主键 -->
<setting name="useGeneratedKeys" value="true"/>
<!-- 批量操作时用 BATCH 执行器 -->
<setting name="defaultExecutorType" value="BATCH"/>
</settings>

<typeAliases>:类型别名(简化配置)

核心作用

为 Java 类的全类名定义简短别名,在 resultTypeparameterType 等配置中替代全类名,减少冗余,提升可读性。

两种配置方式

(1)方式 1:单个类配置(typeAlias
1
2
3
4
5
6
7
8
9
<typeAliases>
<!-- type:全类名;alias:自定义别名(大小写不敏感) -->
<typeAlias type="com.example.pojo.User" alias="MyUser"/>
</typeAliases>

<!-- 使用别名:resultType 直接用 MyUser -->
<select id="getUserById" resultType="MyUser">
SELECT * FROM t_user WHERE id = #{id}
</select>
(2)方式 2:批量包扫描(推荐)

为指定包下的所有类自动生成别名,默认别名为类名首字母小写(如 Useruser):

1
2
3
4
5
6
7
8
9
<typeAliases>
<!-- 扫描 com.example.pojo 包下所有类 -->
<package name="com.example.pojo"/>
</typeAliases>

<!-- 使用默认别名:resultType 用 user -->
<select id="getUserById" resultType="user">
SELECT * FROM t_user WHERE id = #{id}
</select>
(3)方式 3:注解自定义别名(@Alias

在 Java 类上用 @Alias 注解自定义别名,优先级高于包扫描的默认规则:

1
2
3
4
5
6
7
8
9
10
11
12
// 注解定义别名,覆盖默认的 "user"
@Alias("CustomerUser")
public class User {
private Long id;
private String userName;
// getter/setter
}

<!-- 使用注解别名 -->
<select id="getUserById" resultType="CustomerUser">
SELECT * FROM t_user WHERE id = #{id}
</select>

系统内置别名

MyBatis 为常用 Java 类型预定义了别名(无需配置即可使用),例如:

  • 基本类型:int_intlong_long
  • 包装类型:IntegerintegerLonglong
  • 集合类型:ListlistMapmap
  • 时间类型:DatedateLocalDateTimelocaldatetime

示例:

1
2
3
4
<!-- 使用内置别名:resultType 为 list(对应 List),泛型为 user -->
<select id="getAllUsers" resultType="list">
SELECT * FROM t_user
</select>

<typeHandlers>:类型处理器(数据类型转换)

核心作用

负责 Java 类型(JavaType)JDBC 类型(JdbcType) 之间的转换:

  • 入参转换:将 Java 对象的参数(如 String)转换为 JDBC 支持的类型(如 VARCHAR),用于 PreparedStatement.setXXX()
  • 结果转换:将数据库返回的 JDBC 类型(如 INT)转换为 Java 对象(如 Integer),用于 ResultSet.getXXX()

系统内置类型处理器

MyBatis 为绝大多数常用类型提供了默认处理器(无需配置),例如:

  • StringTypeHandlerStringVARCHAR
  • IntegerTypeHandlerIntegerINTEGER
  • LocalDateTimeTypeHandlerLocalDateTimeTIMESTAMP

自定义类型处理器(实战场景)

当内置处理器无法满足需求时(如枚举、JSON 类型),需自定义处理器。以下以 MySQL JSON 字段映射到 Java Map 为例:

(1)自定义 TypeHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.alibaba.fastjson.JSONObject;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

// 1. 注解指定映射的 JavaType 和 JdbcType
@MappedTypes({Map.class}) // Java 类型:Map
@MappedJdbcTypes({JdbcType.VARCHAR}) // JDBC 类型:MySQL 的 JSON 字段在 JDBC 中映射为 VARCHAR
public class MapJsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {

// 2. 入参转换:Java Map → JSON 字符串
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
String jsonStr = JSONObject.toJSONString(parameter);
ps.setString(i, jsonStr);
}

// 3. 结果转换:JSON 字符串 → Java Map(按列名)
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String jsonStr = rs.getString(columnName);
return jsonStr == null ? null : JSONObject.parseObject(jsonStr, Map.class);
}

// 4. 结果转换:JSON 字符串 → Java Map(按列索引)
@Override
public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String jsonStr = rs.getString(columnIndex);
return jsonStr == null ? null : JSONObject.parseObject(jsonStr, Map.class);
}

// 5. 存储过程结果转换
@Override
public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String jsonStr = cs.getString(columnIndex);
return jsonStr == null ? null : JSONObject.parseObject(jsonStr, Map.class);
}
}
(2)注册自定义处理器
1
2
3
<typeHandlers>
<typeHandler handler="com.example.handler.MapJsonTypeHandler"/>
</typeHandlers>
(3)使用示例
  • 数据库表:t_userext_info 字段(类型为 JSON);

  • Mapper XML:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <insert id="insertUser">
    INSERT INTO t_user (user_name, ext_info)
    VALUES (#{userName}, #{extInfo, typeHandler=com.example.handler.MapJsonTypeHandler})
    </insert>

    <select id="getUserById" resultType="user">
    SELECT id, user_name, ext_info
    FROM t_user
    WHERE id = #{id}
    <!-- 结果自动通过 MapJsonTypeHandler 转换为 Map -->
    </select>

<plugins>:插件(扩展 MyBatis 功能)

核心作用

通过拦截 MyBatis 的核心组件(ExecutorParameterHandlerResultSetHandlerStatementHandler),实现自定义逻辑,如分页、日志、性能监控等。

插件开发原理

插件需实现 org.apache.ibatis.plugin.Interceptor 接口,并通过 @Intercepts 注解指定拦截的组件和方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;

// 1. 指定拦截的组件和方法:拦截 Executor 的 query 方法
@Intercepts({
@Signature(
type = Executor.class, // 拦截的组件:执行器
method = "query", // 拦截的方法名
args = {org.apache.ibatis.mapping.MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} // 方法参数类型
)
})
public class QueryLogPlugin implements Interceptor {

// 2. 拦截逻辑:执行 query 方法前打印 SQL 日志
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取拦截的方法参数(MappedStatement 包含 SQL 信息)
Object[] args = invocation.getArgs();
org.apache.ibatis.mapping.MappedStatement ms = (org.apache.ibatis.mapping.MappedStatement) args[0];
System.out.println("执行 SQL:" + ms.getSqlSource().getBoundSql(args[1]).getSql());

// 执行原方法(放行)
return invocation.proceed();
}

// 3. 生成代理对象(MyBatis 内部调用)
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

// 4. 设置插件属性(从 XML 配置中读取)
@Override
public void setProperties(Properties properties) {
// 例如读取配置的 logLevel:properties.getProperty("logLevel")
}
}

注册插件

1
2
3
4
5
6
<plugins>
<plugin interceptor="com.example.plugin.QueryLogPlugin">
<!-- 配置插件属性(可选) -->
<property name="logLevel" value="INFO"/>
</plugin>
</plugins>

常用第三方插件

  • PageHelper:MyBatis 最流行的分页插件,支持 MySQL、Oracle 等多种数据库的分页查询;

    配置示例:

1
2
3
4
5
6
7
8
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 配置数据库方言 -->
<property name="helperDialect" value="mysql"/>
<!-- 支持 PageHelper.startPage() 自动分页 -->
<property name="supportMethodsArguments" value="true"/>
</plugin>
</plugins>
  • MyBatis-Plus:基于 MyBatis 的增强工具,内置分页、CRUD 接口等功能,无需编写 XML。

<environments>:环境配置(多数据源支持)

核心作用

配置数据库连接环境(如开发、测试、生产),每个环境包含 事务管理器数据源 两部分。MyBatis 允许配置多个环境,但每个 SqlSessionFactory 只能选择一个环境。

关键配置项

(1)事务管理器(transactionManager

MyBatis 提供两种事务管理器:

  • JDBC:依赖 JDBC 的事务管理(java.sql.Connection 的 commit/rollback),适用于独立使用 MyBatis 的场景;
  • MANAGED:依赖外部容器(如 Spring、Java EE)管理事务,MyBatis 不干预事务提交 / 回滚,适用于 Spring 整合场景。

示例:

1
2
<transactionManager type="JDBC"/> <!-- 独立使用 MyBatis -->
<!-- <transactionManager type="MANAGED"/> --> <!-- Spring 整合时使用 -->
(2)数据源(dataSource

MyBatis 提供三种数据源类型:

类型 核心特点 适用场景
UNPOOLED 无连接池,每次请求创建 / 关闭连接 开发环境、低并发场景
POOLED 内置连接池,复用连接,提升性能 生产环境、中低并发场景
JNDI 从 JNDI 容器获取数据源(如 Tomcat 的数据源) Java EE 容器环境

生产环境推荐:使用第三方连接池(如 Druid、HikariCP)替代 MyBatis 内置数据源,性能更优。以下是整合 Druid 的示例:

  1. 引入 Druid 依赖(Maven):

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.20</version>
    </dependency>
  2. 配置 Druid 数据源:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <dataSource type="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <!-- Druid 连接池参数 -->
    <property name="maxActive" value="20"/> <!-- 最大活跃连接数 -->
    <property name="minIdle" value="5"/> <!-- 最小空闲连接数 -->
    <property name="maxWait" value="60000"/> <!-- 获取连接超时时间(毫秒) -->
    <property name="validationQuery" value="SELECT 1"/> <!-- 连接校验 SQL -->
    </dataSource>

多环境配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<environments default="development">
<!-- 开发环境 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${dev.jdbc.driver}"/>
<property name="url" value="${dev.jdbc.url}"/>
<property name="username" value="${dev.jdbc.username}"/>
<property name="password" value="${dev.jdbc.password}"/>
</dataSource>
</environment>

<!-- 生产环境 -->
<environment id="production">
<transactionManager type="MANAGED"/> <!-- Spring 管理事务 -->
<dataSource type="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prod.jdbc.driver}"/>
<property name="url" value="${prod.jdbc.url}"/>
<property name="username" value="${prod.jdbc.username}"/>
<property name="password" value="${prod.jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 构建 SqlSessionFactory 时指定生产环境 -->
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is, "production");

<databaseIdProvider>:多数据库适配

核心作用

根据数据库厂商(如 MySQL、Oracle)自动选择对应的 SQL 语句,解决多数据库兼容问题(如分页语法差异)。

配置示例

(1)配置数据库标识
1
2
3
4
5
6
7
<databaseIdProvider type="DB_VENDOR">
<!-- name:DatabaseMetaData.getDatabaseProductName() 返回的厂商名(如 "MySQL"、"Oracle") -->
<!-- value:自定义别名,用于 mapper.xml 中的 databaseId 属性 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
(2)Mapper XML 中适配多数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- MySQL 分页:使用 LIMIT -->
<select id="getUserList" resultType="user" databaseId="mysql">
SELECT * FROM t_user LIMIT #{offset}, #{pageSize}
</select>

<!-- Oracle 分页:使用 ROWNUM -->
<select id="getUserList" resultType="user" databaseId="oracle">
SELECT * FROM (
SELECT u.*, ROWNUM rn FROM t_user u WHERE ROWNUM <= #{offset} + #{pageSize}
) WHERE rn > #{offset}
</select>

<!-- 通用 SQL:无 databaseId,当没有匹配的数据库标识时使用 -->
<select id="getUserById" resultType="user">
SELECT * FROM t_user WHERE id = #{id}
</select>

匹配规则

MyBatis 加载 SQL 语句的优先级:

  1. 优先加载 databaseId 与当前数据库标识一致的语句;
  2. 若未找到,加载无 databaseId 的语句;
  3. 若同时存在带 databaseId 和不带的语句,不带的会被舍弃。

<mappers>:映射器注册(关联 Mapper 接口与 XML)

核心作用

将 Mapper 接口(如 UserMapper.java)与 SQL 映射文件(如 UserMapper.xml)注册到 MyBatis,确保 MyBatis 能找到并执行 SQL 语句。

四种注册方式

方式 配置语法 注意事项
resource <mapper resource="mapper/UserMapper.xml"/> 基于类路径(src/main/resources),推荐用于 XML 映射文件;路径用 / 分隔。
url <mapper url="file:///D:/mapper/UserMapper.xml"/> 基于磁盘 / 网络路径,需指定完整 URL(如 file:http:),不推荐工程使用。
class <mapper class="com.example.mapper.UserMapper"/> 基于 Mapper 接口: 1. 接口与 XML 需同名且同路径; 2. 若用注解(如 @Select),可无 XML。
package <package name="com.example.mapper"/> 批量注册包下所有 Mapper 接口: 1. 接口与 XML 需同名且同路径; 2. 最常用,简化配置。

工程实践(Spring Boot 整合)

Spring Boot 中无需在 mybatis-config.xml 中注册 Mapper,直接通过注解扫描:

1
2
3
4
5
6
7
8
// 启动类添加 @MapperScan,批量扫描 Mapper 接口
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描 com.example.mapper 包下所有接口
public class MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisApplication.class, args);
}
}

常见问题排查

  • Mapper 接口未注册:报错No qualifying bean of type ‘com.example.mapper.UserMapper’ available,检查:
    1. @MapperScan 扫描路径是否正确;
    2. Mapper 接口是否加了 @Mapper 注解(若未用 @MapperScan);
    3. 接口与 XML 是否同名且同路径。

总结:全局配置文件工程最佳实践

  1. 配置顺序不可乱:严格按 properties→settings→typeAliases→...→mappers 排列;
  2. 外部化配置优先:数据库信息用外部文件(如 application.yml),避免硬编码;
  3. 关键 settings 必配mapUnderscoreToCamelCaseuseGeneratedKeyslogImpl 等强制开启,提升开发效率;
  4. 批量注册 Mapper:用 <package>@MapperScan 批量注册,减少配置量;
  5. 生产环境用第三方连接池: Druid 或 HikariCP 替代 MyBatis 内置数据源,提升性能;
  6. 多数据库适配:用 databaseIdProvider 解决 SQL 语法差异,避免分支代码

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10