Spring 整合 MyBatis 详细教程(含代码解析 + 原理)
Spring 整合 MyBatis 的核心目标是:让 Spring 管理 MyBatis 的核心组件(SqlSessionFactory、Mapper 接口代理对象),简化配置(如数据源、事务),并实现依赖注入。
整体流程:引入依赖 → 配置核心资源(数据源、SqlSessionFactory、Mapper 扫描) → 编写 Mapper 接口+SQL 映射 → Service 层调用 Mapper → 测试
一、环境准备:引入依赖(Maven 示例)
需引入 4 类核心依赖:Spring 核心、Spring 事务、MyBatis、MyBatis-Spring 整合包、数据库驱动(以 MySQL 8 为例)。
XML
<dependencies>
<!-- 1. Spring 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version> <!-- 与 Spring 其他组件版本一致 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version> <!-- 用于 Spring 管理数据源、事务 -->
</dependency>
<!-- 2. MyBatis 核心依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- 3. MyBatis-Spring 整合包(关键!实现两者通信) -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version> <!-- 需与 MyBatis、Spring 版本兼容 -->
</dependency>
<!-- 4. 数据库驱动(MySQL 8) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>runtime</scope>
</dependency>
<!-- 5. 连接池(Spring 内置的 HikariCP,性能最优) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!-- 可选:Spring 测试、Lombok 简化代码 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<optional>true</optional>
</dependency>
</dependencies>
二、核心配置:Spring 配置文件(applicationContext.xml)
核心是配置 3 个关键 Bean:数据源(DataSource)→ SqlSessionFactory → Mapper 扫描器,让 Spring 接管 MyBatis 组件。
XML
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1. 开启组件扫描(扫描 @Service、@Repository 等注解) -->
<context:component-scan base-package="com.example"/>
<!-- 2. 配置数据源(HikariCP,Spring 推荐) -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<!-- MySQL 8 驱动类 -->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<!-- 连接URL(useSSL=false 关闭SSL,serverTimezone指定时区) -->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/> <!-- 你的数据库用户名 -->
<property name="password" value="123456"/> <!-- 你的数据库密码 -->
<!-- 连接池参数(可选配置) -->
<property name="maximumPoolSize" value="10"/> <!-- 最大连接数 -->
<property name="minimumIdle" value="5"/> <!-- 最小空闲连接数 -->
</bean>
<!-- 3. 配置 SqlSessionFactory(MyBatis 核心工厂,由 Spring 管理) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源(必须,关联数据库连接) -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置 MyBatis 全局配置文件(可选,若有 mybatis-config.xml 则指定路径) -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 配置 SQL 映射文件路径(Mapper.xml 文件所在位置) -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<!-- 配置实体类别名(可选,简化 Mapper.xml 中 resultType 写法) -->
<property name="typeAliasesPackage" value="com.example.entity"/>
</bean>
<!-- 4. 配置 Mapper 扫描器(关键!自动生成 Mapper 接口的代理对象) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入 SqlSessionFactory(关联工厂,生成代理对象) -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 扫描 Mapper 接口所在包(会自动为该包下的接口生成代理对象) -->
<property name="basePackage" value="com.example.mapper"/>
</bean>
<!-- 5. 配置事务管理器(Spring 声明式事务,可选但推荐) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源(事务基于数据源管理) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解驱动(支持 @Transactional 注解) -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
补充:MyBatis 全局配置文件(mybatis-config.xml,可选)
用于配置 MyBatis 全局特性(如日志、驼峰命名自动转换),若无需复杂配置可省略。
XML
<?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>
<!-- 配置日志(可选,打印 SQL 语句方便调试) -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/> <!-- 控制台打印 SQL -->
<setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 下划线转驼峰(如 user_name → userName) -->
</settings>
</configuration>
三、项目结构设计(规范分层)
按 "实体层 → Mapper 层 → Service 层 → 测试层" 分层,结构清晰:
com.example
├── entity # 实体类(与数据库表对应)
│ └── User.java
├── mapper # Mapper 接口(MyBatis DAO 层)
│ ├── UserMapper.java # 接口(无实现类,MyBatis 动态代理)
│ └── UserMapper.xml # SQL 映射文件
├── service # Service 层(业务逻辑)
│ ├── UserService.java # 接口
│ └── impl
│ └── UserServiceImpl.java # 实现类
├── config # 配置文件(可选,若用注解配置)
└── test # 测试类
└── UserServiceTest.java
四、分层代码实现(逐步解析)
1. 实体层(Entity):与数据库表映射
假设数据库有 user 表:
sql
CREATE TABLE `user` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`user_name` varchar(50) NOT NULL,
`age` int NOT NULL,
`email` varchar(100)
);
对应实体类 User.java(用 Lombok 简化 getter/setter):
java
package com.example.entity;
import lombok.Data; // Lombok 注解,自动生成 getter/setter/toString 等
@Data // 等价于写 getter、setter、toString、equals、hashCode
public class User {
private Integer id; // 对应表中 id(主键)
private String userName; // 对应表中 user_name(驼峰命名,需开启 mapUnderscoreToCamelCase)
private Integer age; // 对应表中 age
private String email; // 对应表中 email
}
2. Mapper 层:MyBatis 数据访问层
(1)Mapper 接口(UserMapper.java)
无需写实现类,MyBatis 会通过动态代理生成代理对象(由 Spring 管理):
java
package com.example.mapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// 无需加 @Repository,MapperScanner 会自动扫描并注册为 Bean
public interface UserMapper {
// 1. 根据 ID 查询用户
User selectById(@Param("id") Integer id); // @Param 用于指定 SQL 中参数名
// 2. 查询所有用户
List<User> selectAll();
// 3. 新增用户(返回受影响行数)
int insert(User user);
// 4. 更新用户
int update(User user);
// 5. 根据 ID 删除用户
int deleteById(@Param("id") Integer id);
}
(2)SQL 映射文件(UserMapper.xml)
放在 resources/mapper/ 目录下,与 Mapper 接口同名,绑定接口的方法:
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">
<!-- namespace 必须等于 Mapper 接口的全类名(绑定接口) -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 1. 根据 ID 查询用户 -->
<!-- id 必须等于接口方法名,resultType 是返回的实体类(已配置别名,直接写类名) -->
<select id="selectById" resultType="User">
SELECT id, user_name, age, email FROM user WHERE id = #{id}
</select>
<!-- 2. 查询所有用户 -->
<select id="selectAll" resultType="User">
SELECT id, user_name, age, email FROM user
</select>
<!-- 3. 新增用户 -->
<!-- useGeneratedKeys="true" 开启主键自增,keyProperty="id" 绑定实体类的 id 属性 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (user_name, age, email)
VALUES (#{userName}, #{age}, #{email})
</insert>
<!-- 4. 更新用户 -->
<update id="update">
UPDATE user
SET user_name = #{userName}, age = #{age}, email = #{email}
WHERE id = #{id}
</update>
<!-- 5. 删除用户 -->
<delete id="deleteById">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
关键解析:
namespace:必须与 Mapper 接口全类名一致,否则 MyBatis 无法绑定接口和 SQL。id:必须与接口方法名一致,MyBatis 通过 "接口全类名 + 方法名" 定位 SQL。resultType:返回结果类型(已配置typeAliasesPackage,可直接写实体类名,无需全类名)。#{}:参数占位符(预编译,防止 SQL 注入),对应实体类的属性名或@Param指定的参数名。
3. Service 层:业务逻辑层
(1)Service 接口(UserService.java)
java
package com.example.service;
import com.example.entity.User;
import java.util.List;
public interface UserService {
// 根据 ID 查询用户
User getUserById(Integer id);
// 查询所有用户
List<User> getAllUsers();
// 新增用户
boolean addUser(User user);
// 更新用户
boolean updateUser(User user);
// 删除用户
boolean deleteUser(Integer id);
}
(2)Service 实现类(UserServiceImpl.java)
通过 @Service 注册为 Spring Bean,通过 @Autowired 注入 Mapper 代理对象(Spring 已管理):
java
package com.example.service.impl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; // 事务注解
import java.util.List;
@Service // 标记为 Spring 服务层 Bean,会被 component-scan 扫描到
public class UserServiceImpl implements UserService {
// 注入 Mapper 接口的代理对象(Spring 自动生成,无需手动创建)
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Integer id) {
// 直接调用 Mapper 方法(代理对象会自动执行 SQL)
return userMapper.selectById(id);
}
@Override
public List<User> getAllUsers() {
return userMapper.selectAll();
}
@Override
@Transactional // 标记该方法需要事务(失败时回滚)
public boolean addUser(User user) {
// 调用 Mapper 新增方法,返回受影响行数(>0 表示成功)
int rows = userMapper.insert(user);
return rows > 0;
}
@Override
@Transactional
public boolean updateUser(User user) {
int rows = userMapper.update(user);
return rows > 0;
}
@Override
@Transactional
public boolean deleteUser(Integer id) {
int rows = userMapper.deleteById(id);
return rows > 0;
}
}
关键解析:
@Service:告诉 Spring 这是服务层 Bean,Spring 会自动创建实例并管理。@Autowired:依赖注入 Mapper 代理对象(Spring 通过 MapperScanner 扫描后,将代理对象注册到容器,此处直接注入即可使用)。@Transactional:声明式事务注解,标记该方法需要事务支持(如新增 / 更新 / 删除操作,失败时自动回滚,避免数据不一致)。
五、测试:验证整合结果
使用 Spring Test 测试 Service 层,验证 MyBatis 是否正常工作。
java
package com.example.test;
import com.example.entity.User;
import com.example.service.UserService;
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 java.util.List;
// 1. 运行器:使用 SpringJUnit4ClassRunner 整合 JUnit 和 Spring
@RunWith(SpringJUnit4ClassRunner.class)
// 2. 加载 Spring 配置文件(指定 applicationContext.xml 路径)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserServiceTest {
// 注入 Service Bean(Spring 容器已创建)
@Autowired
private UserService userService;
// 测试查询所有用户
@Test
public void testGetAllUsers() {
List<User> users = userService.getAllUsers();
users.forEach(System.out::println); // 打印用户列表
}
// 测试新增用户
@Test
public void testAddUser() {
User user = new User();
user.setUserName("张三");
user.setAge(25);
user.setEmail("zhangsan@example.com");
boolean success = userService.addUser(user);
System.out.println("新增是否成功:" + success);
System.out.println("新增用户 ID:" + user.getId()); // 主键自增后,id 会被回写到实体类
}
// 测试根据 ID 查询
@Test
public void testGetUserById() {
User user = userService.getUserById(1); // 假设新增的用户 ID 为 1
System.out.println("查询到的用户:" + user);
}
}
六、核心原理解析(为什么这样整合?)
- SqlSessionFactoryBean :MyBatis-Spring 提供的工厂 Bean,负责创建
SqlSessionFactory(MyBatis 核心工厂),并关联 Spring 管理的数据源,避免手动创建SqlSessionFactory的繁琐。 - MapperScannerConfigurer :扫描指定包下的 Mapper 接口,为每个接口生成
SqlSession管理的代理对象,并将代理对象注册到 Spring 容器,后续可通过@Autowired直接注入。 - 事务管理 :Spring 的
DataSourceTransactionManager接管事务,通过@Transactional注解实现声明式事务,无需手动管理SqlSession的提交 / 回滚。 - 依赖注入:Spring 统一管理数据源、SqlSessionFactory、Mapper 代理对象、Service Bean,实现各层之间的解耦,无需手动创建对象。
七、常见问题排查
- Mapper 接口注入失败(No qualifying bean of type) :
- 检查
MapperScannerConfigurer的basePackage是否正确(需包含 Mapper 接口包)。 - 确保 Mapper 接口没有写实现类(MyBatis 动态代理不需要)。
- 检查
- SQL 映射文件找不到(BindingException) :
- 检查
SqlSessionFactoryBean的mapperLocations路径是否正确(如classpath:mapper/*.xml)。 - 确保
UserMapper.xml的namespace与 Mapper 接口全类名一致。
- 检查
- 数据库连接失败(SQLException) :
- 检查数据源的
jdbcUrl、username、password是否正确。 - MySQL 8 需用
com.mysql.cj.jdbc.Driver驱动,且指定serverTimezone。
- 检查数据源的
- 驼峰命名不生效(如 user_name 无法映射到 userName) :
- 在
mybatis-config.xml中开启mapUnderscoreToCamelCase配置。
- 在
八、扩展:注解式 SQL(无需 Mapper.xml)
若不想写 XML 文件,可使用 MyBatis 注解直接在 Mapper 接口上写 SQL,简化配置:
java
package com.example.mapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
@Select("SELECT id, user_name, age, email FROM user WHERE id = #{id}")
User selectById(@Param("id") Integer id);
@Select("SELECT id, user_name, age, email FROM user")
List<User> selectAll();
@Insert("INSERT INTO user (user_name, age, email) VALUES (#{userName}, #{age}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 主键自增配置
int insert(User user);
@Update("UPDATE user SET user_name = #{userName}, age = #{age}, email = #{email} WHERE id = #{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteById(@Param("id") Integer id);
}
此时可删除 Mapper.xml 文件,同时删除 SqlSessionFactoryBean 中 mapperLocations 配置。