0%

mybatis整合spring

MyBatis 整合 Spring 完整指南:从 XML 配置到 Spring Boot 实践

MyBatis 与 Spring 的整合是企业级开发的主流方案,核心是将 MyBatis 的核心组件(SqlSessionFactoryMapper 代理)交给 Spring IOC 容器管理,同时复用 Spring 的事务管理、依赖注入等能力。 Spring Boot 注解式整合(主流方案)生产级数据源配置事务深化工程规范常见问题排查,覆盖传统 Spring 与 Spring Boot 两种场景。

版本选型与依赖优化

2024 年稳定版本组合(支持 Spring 5 特性,兼容 JDK 8+):

传统 Spring(XML 配置)依赖

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<dependencies>
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!-- MyBatis-Spring 整合包(2.x 支持 Spring 5) -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.6</version>
</dependency>

<!-- Spring 核心依赖(5.x 稳定版) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.31</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.31</version>
<scope>test</scope>
</dependency>

<!-- 生产级数据源(HikariCP,Spring 5 默认) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
<!-- MySQL 驱动(8.x 支持 MySQL 8.0+) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>

<!-- 测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

Spring Boot(注解式)依赖

Spring Boot 提供 mybatis-spring-boot-starter,自动整合 MyBatis 与 Spring,无需手动配置 SqlSessionFactoryMapperScanner,是当前主流方案:

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
<dependencies>
<!-- Spring Boot 父工程(统一版本管理) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version> <!-- 稳定版,兼容 JDK 8-17 -->
</parent>

<!-- MyBatis-Spring Boot starter(自动配置核心) -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>

<!-- Spring Boot 数据源 starter(默认集成 HikariCP) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

<!-- Spring Boot 测试 starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

传统 Spring 整合(XML 配置)深化

1. 核心配置文件

(1)数据库配置文件(jdbc.properties
1
2
3
4
5
6
7
8
9
10
11
# HikariCP 连接池配置(生产级参数)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456

# HikariCP 连接池参数(关键优化)
spring.datasource.hikari.maximum-pool-size=10 # 最大连接数(根据并发调整)
spring.datasource.hikari.minimum-idle=2 # 最小空闲连接数
spring.datasource.hikari.idle-timeout=300000 # 空闲连接超时时间(5分钟)
spring.datasource.hikari.connection-timeout=30000 # 连接超时时间(30秒)
(2)Spring 配置文件(applicationContext.xml
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 1. 扫描业务组件(Service/Controller) -->
<context:component-scan base-package="com.zhanghe.study.mybatis.service"/>

<!-- 2. 加载数据库配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 3. 配置生产级数据源(HikariCP)替代 DriverManagerDataSource -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="${spring.datasource.driver-class-name}"/>
<property name="jdbcUrl" value="${spring.datasource.url}"/>
<property name="username" value="${spring.datasource.username}"/>
<property name="password" value="${spring.datasource.password}"/>
<!-- HikariCP 特有参数 -->
<property name="maximumPoolSize" value="${spring.datasource.hikari.maximum-pool-size}"/>
<property name="minimumIdle" value="${spring.datasource.hikari.minimum-idle}"/>
<property name="idleTimeout" value="${spring.datasource.hikari.idle-timeout}"/>
<property name="connectionTimeout" value="${spring.datasource.hikari.connection-timeout}"/>
</bean>

<!-- 4. 配置事务管理器(Spring JDBC 事务) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 5. 开启事务注解驱动(支持 @Transactional) -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

<!-- 6. 配置 SqlSessionFactory(MyBatis 核心) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 可选:MyBatis 全局配置文件(可省略,直接通过属性配置) -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- Mapper 映射文件扫描(支持通配符,扫描所有子目录下的 XML) -->
<property name="mapperLocations" value="classpath:mapper/**/*.xml"/>
<!-- 简化配置:直接在此处配置 MyBatis 全局参数,无需单独 mybatis-config.xml -->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<!-- 开启驼峰命名映射 -->
<property name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启 SQL 日志 -->
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
<!-- 开启二级缓存 -->
<property name="cacheEnabled" value="true"/>
</bean>
</property>
<!-- 配置类型别名(无需在 mybatis-config.xml 中重复配置) -->
<property name="typeAliasesPackage" value="com.zhanghe.study.mybatis.model"/>
</bean>

<!-- 7. 扫描 Mapper 接口,生成代理对象并交给 Spring 管理 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- Mapper 接口所在包 -->
<property name="basePackage" value="com.zhanghe.study.mybatis.mapper"/>
<!-- 关联 SqlSessionFactory(Spring 5 需用 beanName 而非直接引用,避免循环依赖) -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
(3)简化 MyBatis 全局配置(mybatis-config.xml

由于大部分配置已在 SqlSessionFactoryBean 中完成,mybatis-config.xml 可简化为仅保留必要配置(如数据库厂商标识):

1
2
3
4
5
6
7
8
9
10
11
<?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>
<!-- 数据库厂商标识(多数据库适配) -->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
</configuration>

2. 事务管理深化

Spring 事务管理是整合的核心优势之一,需明确 事务传播行为隔离级别 的使用场景:

(1)Service 层事务示例
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
@Service
public class UserService {
// 注入 Mapper(Spring 自动生成代理对象)
@Autowired
private UserMapper userMapper;

// 声明事务:默认传播行为 REQUIRED,隔离级别 DEFAULT
@Transactional
public void addUserWithRole(User user, List<Integer> roleIds) {
// 1. 插入用户
userMapper.insertUser(user);
// 2. 插入用户-角色关联(若此处抛异常,用户插入会回滚)
for (Integer roleId : roleIds) {
userMapper.insertUserRole(user.getId(), roleId);
}
}

// 只读事务(优化查询性能,避免事务日志开销)
@Transactional(readOnly = true, timeout = 5) // 超时时间 5 秒
public User getUserById(Integer id) {
return userMapper.selectUser(id);
}

// 自定义事务传播行为(如 REQUIRES_NEW:新建事务,与父事务独立)
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void logUserOperation(UserOperateLog log) {
userMapper.insertOperateLog(log);
}
}
(2)事务关键属性说明
属性 作用 常用值
propagation 事务传播行为(如何处理嵌套事务) REQUIRED(默认)、REQUIRES_NEWSUPPORTS
isolation 事务隔离级别(解决脏读、不可重复读、幻读) DEFAULT(默认,跟随数据库)、READ_COMMITTED
readOnly 是否为只读事务(查询操作建议开启,优化性能) false(默认)、true
timeout 事务超时时间(超过时间自动回滚) 整数(秒),默认 -1(无超时)
rollbackFor 触发回滚的异常类型(默认仅回滚运行时异常) Exception.class(所有异常回滚)

3. 测试类优化(Spring 测试框架)

使用 SpringJUnit4ClassRunner 自动加载 Spring 容器,避免手动创建 ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.zhanghe.study.mybatis.mapper.UserMapper;
import com.zhanghe.study.mybatis.model.User;

// 整合 Spring 测试框架
@RunWith(SpringJUnit4ClassRunner.class)
// 加载 Spring 配置文件
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MyBatisSpringTest {

// 自动注入 Mapper(Spring 管理的代理对象)
@Autowired
private UserMapper userMapper;

@Test
public void testSelectUser() {
User user = userMapper.selectUser(8);
System.out.println(user); // 直接使用 Mapper,无需手动创建 SqlSession
}
}

Spring Boot 整合(注解式,主流方案)

Spring Boot 通过 自动配置 简化整合,无需 XML 配置,核心是 application.yml 配置和注解扫描。

1. 核心配置文件(application.yml

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
# 服务器配置(可选)
server:
port: 8080

# 数据库与数据源配置(HikariCP 自动集成)
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: 123456
# HikariCP 连接池参数
hikari:
maximum-pool-size: 10
minimum-idle: 2
idle-timeout: 300000
connection-timeout: 30000

# MyBatis 配置(替代 mybatis-config.xml)
mybatis:
# Mapper 映射文件位置
mapper-locations: classpath:mapper/**/*.xml
# 类型别名扫描包
type-aliases-package: com.zhanghe.study.mybatis.model
# 全局配置(对应 mybatis-config.xml 的 settings)
configuration:
map-underscore-to-camel-case: true # 驼峰命名映射
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL 日志
cache-enabled: true # 开启二级缓存

# 日志配置(可选,细化日志级别)
logging:
level:
com.zhanghe.study.mybatis.mapper: debug # Mapper 接口日志级别设为 debug,打印 SQL

2. 启动类配置(MyBatisApplication.java

通过 @MapperScan 扫描 Mapper 接口,替代传统 XML 中的 MapperScannerConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Spring Boot 应用入口
@SpringBootApplication
// 扫描 Mapper 接口(无需在每个 Mapper 上加 @Mapper 注解)
@MapperScan("com.zhanghe.study.mybatis.mapper")
public class MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisApplication.class, args);
}
}

3. 组件实现(与传统 Spring 一致)

(1)实体类(User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.zhanghe.study.mybatis.model;

import java.io.Serializable;
import java.util.Date;

// 实现 Serializable 用于 MyBatis 二级缓存(readOnly=false 时需序列化)
public class User implements Serializable {
private Integer id;
private String userName; // 对应数据库 user_name(驼峰映射生效)
private Integer age;
private Date createTime;

// getter/setter、toString
}
(2)Mapper 接口(UserMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zhanghe.study.mybatis.mapper;

import com.zhanghe.study.mybatis.model.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {
// 插入用户(返回自增主键)
int insertUser(User user);

// 多参数查询(@Param 绑定参数名)
User selectUserByUsernameAndAge(@Param("userName") String userName, @Param("age") Integer age);

// 根据 ID 查询用户
User selectUser(@Param("id") Integer id);
}
(3)Mapper 映射文件(UserMapper.xml

放在 src/main/resources/mapper/ 目录下:

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
<?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.zhanghe.study.mybatis.mapper.UserMapper">
<!-- 二级缓存配置(可选) -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="false"/>

<!-- 插入用户,返回自增主键 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (user_name, age, create_time)
VALUES (#{userName}, #{age}, #{createTime})
</insert>

<!-- 根据用户名和年龄查询 -->
<select id="selectUserByUsernameAndAge" resultType="User">
SELECT id, user_name, age, create_time
FROM user
WHERE user_name = #{userName} AND age = #{age}
</select>

<!-- 根据 ID 查询 -->
<select id="selectUser" resultType="User">
SELECT id, user_name, age, create_time
FROM user
WHERE id = #{id}
</select>
</mapper>

4. 测试类(Spring Boot Test)

使用 @SpringBootTest 自动加载 Spring Boot 上下文:

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
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.zhanghe.study.mybatis.mapper.UserMapper;
import com.zhanghe.study.mybatis.model.User;
import java.util.Date;

// Spring Boot 测试注解(自动加载启动类)
@SpringBootTest
public class MyBatisBootTest {

@Autowired
private UserMapper userMapper;

@Test
public void testInsertUser() {
User user = new User();
user.setUserName("Spring Boot");
user.setAge(3);
user.setCreateTime(new Date());

int rows = userMapper.insertUser(user);
System.out.println("插入行数:" + rows);
System.out.println("自增主键:" + user.getId()); // 自动获取主键
}

@Test
public void testSelectUser() {
User user = userMapper.selectUser(1);
System.out.println(user); // 输出用户信息,SQL 日志自动打印
}
}

整合核心原理(简化版)

分析 SqlSessionFactoryBeanMapperScannerConfigurer 的源码,以下提炼核心逻辑,帮助理解整合本质:

1. SqlSessionFactoryBean:构建 SqlSessionFactory

  • 作用:替代 MyBatis 原生的 SqlSessionFactoryBuilder,将 Spring 管理的 DataSource 与 MyBatis 配置整合,生成 SqlSessionFactory
  • 核心流程:
    1. 读取 dataSource(Spring 数据源)、mapperLocations(Mapper 映射文件)等配置;
    2. 构建 MyBatis 的 Configuration 对象(封装全局配置、Mapper 信息);
    3. 调用 SqlSessionFactoryBuilder.build(configuration) 生成 SqlSessionFactory
    4. 通过 FactoryBean 接口的 getObject() 方法,将 SqlSessionFactory 交给 Spring 容器。

2. MapperScannerConfigurer:生成 Mapper 代理对象

  • 作用:扫描指定包下的 Mapper 接口,为每个接口生成 JDK 动态代理对象,并注册到 Spring 容器。
  • 核心流程:
    1. 扫描 basePackage 下的所有接口(如 UserMapper);
    2. 为每个接口创建 MapperFactoryBean(实现 FactoryBean);
    3. MapperFactoryBean 通过 SqlSession 获取 Mapper 代理对象(sqlSession.getMapper(xxx.class));
    4. Spring 容器中注入的 UserMapper 实际是 MapperFactoryBean 生成的代理对象,调用方法时底层执行 SQL。

常见问题与解决方案

1. Mapper 注入失败(No qualifying bean of type UserMapper available

  • 原因 1:未扫描 Mapper 接口(传统 Spring 未配置 MapperScannerConfigurer,Spring Boot 未加 @MapperScan);
  • 原因 2:Mapper 接口与映射文件 不同包同名(如接口在 com.mapper,映射文件需在 resources/com/mapper 下,且文件名一致);
  • 解决方案:
    • 传统 Spring:确保 MapperScannerConfigurerbasePackage 正确;
    • Spring Boot:在启动类加 @MapperScan("com.zhanghe.study.mybatis.mapper")

2. 事务不生效(@Transactional 注解无效)

  • 原因 1:未开启事务驱动(传统 Spring 未配置 <tx:annotation-driven>,Spring Boot 需确保引入 spring-boot-starter-tx);

  • 原因 2:事务方法为 非 public 修饰(Spring 事务仅对 public 方法生效);

  • 原因 3:异常类型未纳入回滚(默认仅回滚 RuntimeException,需配置 rollbackFor = Exception.class);

  • 解决方案:

    1
    2
    @Transactional(rollbackFor = Exception.class) // 所有异常回滚
    public void addUser(User user) throws Exception { ... }

3. 连接池配置无效(传统 Spring 仍用默认数据源)

  • 原因:依赖冲突或数据源配置错误(如 HikariCP 依赖未引入,或 jdbcUrl 拼写错误);
  • 解决方案:
    • 确保引入 HikariCP 依赖;
    • 传统 Spring 中 HikariDataSource 的属性是 jdbcUrl,而非 url(区别于 DriverManagerDataSource)。

4. 二级缓存不生效(跨 SqlSession 未命中)

  • 原因 1:实体类未实现 SerializablereadOnly=false 时需序列化克隆对象);
  • 原因 2:未在 Mapper 映射文件中配置 <cache> 标签;
  • 原因 3:查询标签配置 useCache="false"(禁用当前查询的二级缓存);
  • 解决方案:
    • 实体类实现 Serializable
    • UserMapper.xml 中添加 <cache/>

总结

MyBatis 与 Spring 整合的核心是 “Spring 管理 MyBatis 组件”,两种方案各有适用场景:

  • 传统 Spring(XML 配置):适合维护旧项目,配置灵活但繁琐;
  • Spring Boot(注解式):适合新项目,自动配置简化开发,是当前主流

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

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