mybatis整合spring
MyBatis 整合 Spring 完整指南:从 XML 配置到 Spring Boot 实践
MyBatis 与 Spring 的整合是企业级开发的主流方案,核心是将 MyBatis 的核心组件(SqlSessionFactory、Mapper 代理)交给 Spring IOC 容器管理,同时复用 Spring 的事务管理、依赖注入等能力。 Spring Boot 注解式整合(主流方案)、生产级数据源配置、事务深化、工程规范及常见问题排查,覆盖传统 Spring 与 Spring Boot 两种场景。
版本选型与依赖优化
2024 年稳定版本组合(支持 Spring 5 特性,兼容 JDK 8+):
传统 Spring(XML 配置)依赖
<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,无需手动配置 SqlSessionFactory 和 MapperScanner,是当前主流方案:
<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)
# 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)
<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 可简化为仅保留必要配置(如数据库厂商标识):
<configuration>
<!-- 数据库厂商标识(多数据库适配) -->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
</configuration>
2. 事务管理深化
Spring 事务管理是整合的核心优势之一,需明确 事务传播行为 和 隔离级别 的使用场景:
(1)Service 层事务示例
public class UserService {
// 注入 Mapper(Spring 自动生成代理对象)
private UserMapper userMapper;
// 声明事务:默认传播行为 REQUIRED,隔离级别 DEFAULT
public void addUserWithRole(User user, List<Integer> roleIds) {
// 1. 插入用户
userMapper.insertUser(user);
// 2. 插入用户-角色关联(若此处抛异常,用户插入会回滚)
for (Integer roleId : roleIds) {
userMapper.insertUserRole(user.getId(), roleId);
}
}
// 只读事务(优化查询性能,避免事务日志开销)
// 超时时间 5 秒
public User getUserById(Integer id) {
return userMapper.selectUser(id);
}
// 自定义事务传播行为(如 REQUIRES_NEW:新建事务,与父事务独立)
public void logUserOperation(UserOperateLog log) {
userMapper.insertOperateLog(log);
}
}
(2)事务关键属性说明
| 属性 | 作用 | 常用值 |
|---|---|---|
propagation |
事务传播行为(如何处理嵌套事务) | REQUIRED(默认)、REQUIRES_NEW、SUPPORTS |
isolation |
事务隔离级别(解决脏读、不可重复读、幻读) | DEFAULT(默认,跟随数据库)、READ_COMMITTED |
readOnly |
是否为只读事务(查询操作建议开启,优化性能) | false(默认)、true |
timeout |
事务超时时间(超过时间自动回滚) | 整数(秒),默认 -1(无超时) |
rollbackFor |
触发回滚的异常类型(默认仅回滚运行时异常) | Exception.class(所有异常回滚) |
3. 测试类优化(Spring 测试框架)
使用 SpringJUnit4ClassRunner 自动加载 Spring 容器,避免手动创建 ApplicationContext:
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 测试框架
// 加载 Spring 配置文件
public class MyBatisSpringTest {
// 自动注入 Mapper(Spring 管理的代理对象)
private UserMapper userMapper;
public void testSelectUser() {
User user = userMapper.selectUser(8);
System.out.println(user); // 直接使用 Mapper,无需手动创建 SqlSession
}
}
Spring Boot 整合(注解式,主流方案)
Spring Boot 通过 自动配置 简化整合,无需 XML 配置,核心是 application.yml 配置和注解扫描。
1. 核心配置文件(application.yml)
# 服务器配置(可选)
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:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// Spring Boot 应用入口
// 扫描 Mapper 接口(无需在每个 Mapper 上加 @Mapper 注解)
public class MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisApplication.class, args);
}
}
3. 组件实现(与传统 Spring 一致)
(1)实体类(User.java)
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)
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( String userName, Integer age);
// 根据 ID 查询用户
User selectUser( Integer id);
}
(3)Mapper 映射文件(UserMapper.xml)
放在 src/main/resources/mapper/ 目录下:
<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 上下文:
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 测试注解(自动加载启动类)
public class MyBatisBootTest {
private UserMapper userMapper;
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()); // 自动获取主键
}
public void testSelectUser() {
User user = userMapper.selectUser(1);
System.out.println(user); // 输出用户信息,SQL 日志自动打印
}
}
整合核心原理(简化版)
分析 SqlSessionFactoryBean 和 MapperScannerConfigurer 的源码,以下提炼核心逻辑,帮助理解整合本质:
1. SqlSessionFactoryBean:构建 SqlSessionFactory
- 作用:替代 MyBatis 原生的
SqlSessionFactoryBuilder,将 Spring 管理的DataSource与 MyBatis 配置整合,生成SqlSessionFactory。 - 核心流程:
- 读取
dataSource(Spring 数据源)、mapperLocations(Mapper 映射文件)等配置; - 构建 MyBatis 的
Configuration对象(封装全局配置、Mapper 信息); - 调用
SqlSessionFactoryBuilder.build(configuration)生成SqlSessionFactory; - 通过
FactoryBean接口的getObject()方法,将SqlSessionFactory交给 Spring 容器。
- 读取
2. MapperScannerConfigurer:生成 Mapper 代理对象
- 作用:扫描指定包下的 Mapper 接口,为每个接口生成 JDK 动态代理对象,并注册到 Spring 容器。
- 核心流程:
- 扫描
basePackage下的所有接口(如UserMapper); - 为每个接口创建
MapperFactoryBean(实现FactoryBean); MapperFactoryBean通过SqlSession获取 Mapper 代理对象(sqlSession.getMapper(xxx.class));- 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:确保
MapperScannerConfigurer的basePackage正确; - Spring Boot:在启动类加
@MapperScan("com.zhanghe.study.mybatis.mapper")。
- 传统 Spring:确保
2. 事务不生效(@Transactional 注解无效)
原因 1:未开启事务驱动(传统 Spring 未配置
<tx:annotation-driven>,Spring Boot 需确保引入spring-boot-starter-tx);原因 2:事务方法为 非 public 修饰(Spring 事务仅对 public 方法生效);
原因 3:异常类型未纳入回滚(默认仅回滚
RuntimeException,需配置rollbackFor = Exception.class);解决方案:
// 所有异常回滚 public void addUser(User user) throws Exception { ... }
3. 连接池配置无效(传统 Spring 仍用默认数据源)
- 原因:依赖冲突或数据源配置错误(如 HikariCP 依赖未引入,或
jdbcUrl拼写错误); - 解决方案:
- 确保引入 HikariCP 依赖;
- 传统 Spring 中
HikariDataSource的属性是jdbcUrl,而非url(区别于DriverManagerDataSource)。
4. 二级缓存不生效(跨 SqlSession 未命中)
- 原因 1:实体类未实现
Serializable(readOnly=false时需序列化克隆对象); - 原因 2:未在 Mapper 映射文件中配置
<cache>标签; - 原因 3:查询标签配置
useCache="false"(禁用当前查询的二级缓存); - 解决方案:
- 实体类实现
Serializable; - 在
UserMapper.xml中添加<cache/>。
- 实体类实现
总结
MyBatis 与 Spring 整合的核心是 “Spring 管理 MyBatis 组件”,两种方案各有适用场景:
- 传统 Spring(XML 配置):适合维护旧项目,配置灵活但繁琐;
- Spring Boot(注解式):适合新项目,自动配置简化开发,是当前主流