Spring 整合 MyBatis 全流程详解(附 Junit 单元测试实战)(Spring系列6)
摘要:Spring作为Java后端的核心框架,提供了强大的IoC容器来管理Bean;MyBatis是优秀的持久层框架,简化了JDBC的繁琐操作。本文从纯MyBatis开发流程入手,深入分析Spring整合MyBatis的核心思路,一步步带你完成环境搭建、配置编写、代码实现,同时补充Spring整合Junit单元测试的完整方案,附完整代码、项目结构、对比分析,帮你彻底掌握Spring整合MyBatis的原理与实践。
一、纯MyBatis开发流程回顾(环境准备)
在整合Spring之前,我们先回顾纯MyBatis的开发流程,明确MyBatis的核心对象和配置,为后续整合做铺垫。
1.1 环境搭建步骤
步骤1:准备数据库表
MyBatis用于操作数据库,首先创建数据库和表:
create database spring_db character set utf8; use spring_db; create table tbl_account( id int primary key auto_increment, name varchar(35), money double );步骤2:创建项目导入Jar包(pom.xml)
<dependencies> <!-- Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- Druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!-- MyBatis核心依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>步骤3:创建模型类(domain包)
public class Account implements Serializable { private Integer id; private String name; private Double money; // 构造器、getter、setter、toString方法略 }步骤4:创建Dao接口
public interface AccountDao { @Insert("insert into tbl_account(name,money)values(#{name},#{money})") void save(Account account); @Delete("delete from tbl_account where id = #{id} ") void delete(Integer id); @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ") void update(Account account); @Select("select * from tbl_account") List<Account> findAll(); @Select("select * from tbl_account where id = #{id} ") Account findById(Integer id); }步骤5:创建Service接口和实现类
// Service接口 public interface AccountService { void save(Account account); void delete(Integer id); void update(Account account); List<Account> findAll(); Account findById(Integer id); } // Service实现类 @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void save(Account account) { accountDao.save(account); } @Override public void update(Account account){ accountDao.update(account); } @Override public void delete(Integer id) { accountDao.delete(id); } @Override public Account findById(Integer id) { return accountDao.findById(id); } @Override public List<Account> findAll() { return accountDao.findAll(); } }步骤6:添加jdbc.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false jdbc.username=root jdbc.password=root说明:useSSL=false用于关闭MySQL的SSL连接,避免警告。
步骤7:添加MyBatis核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3.dtd"> <configuration> <!-- 读取外部properties配置文件 --> <properties resource="jdbc.properties"></properties> <!-- 别名扫描的包路径 --> <typeAliases> <package name="com.itheima.domain"/> </typeAliases> <!-- 数据源 --> <environments default="mysql"> <environment> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!-- 映射文件扫描包路径 --> <mappers> <package name="com.itheima.dao"/> </mappers> </configuration>步骤8:编写应用程序
public class App { public static void main(String[] args) throws IOException { // 1. 创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 2. 加载SqlMapConfig.xml配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 3. 创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); // 4. 获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 5. 执行SqlSession对象执行查询,获取结果 AccountDao accountDao = sqlSession.getMapper(AccountDao.class); Account ac = accountDao.findById(1); System.out.println(ac); // 6. 释放资源 sqlSession.close(); } }步骤9:运行程序
Account{id=1, name='Tom', money=1000.0} Process finished with exit code 01.2 MyBatis核心对象与整合思路分析
1.2.1 MyBatis核心对象分析
| 对象 | 作用 | 是否需要Spring管理 |
|---|---|---|
| SqlSessionFactoryBuilder | 构建SqlSessionFactory | 无需,用完即弃 |
| SqlSessionFactory | MyBatis核心工厂,创建SqlSession | ✅ 单例,需要交给Spring管理 |
| SqlSession | 会话,相当于数据库连接,获取Mapper | ❌ 线程不安全,每次手动获取 |
| Mapper接口(AccountDao) | 数据层操作接口 | ✅ 需要Spring扫描生成代理对象 |
1.2.2 整合核心思路
Spring整合MyBatis的核心就是两件事:
- Spring管理SqlSessionFactory:将MyBatis的核心工厂交给Spring IoC容器,统一管理,避免手动创建。
- Spring管理Mapper接口的扫描:自动扫描Dao接口,生成代理对象,交给Spring容器,直接@Autowired注入使用。
同时,原来MyBatis配置文件SqlMapConfig.xml中的所有配置,都可以通过Spring的配置类来实现,最终可以完全舍弃该配置文件。
二、Spring整合MyBatis完整步骤
2.1 导入整合依赖
<!-- Spring操作数据库依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- MyBatis提供的Spring整合包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency>说明:mybatis-spring是MyBatis官方提供的Spring整合工具包,封装了SqlSessionFactoryBean、MapperScannerConfigurer等核心类。
2.2 创建Spring主配置类
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") public class SpringConfig { }- @Configuration:标记该类为Spring配置类
- @ComponentScan:组件扫描,扫描项目中的Service、Dao等Bean
- @PropertySource:加载jdbc.properties配置文件
2.3 数据源配置类(Druid连接池)
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }2.4 MyBatis核心配置类
public class MybatisConfig { /** * 配置SqlSessionFactoryBean,生成SqlSessionFactory */ @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); // 设置模型类的别名扫描包 ssfb.setTypeAliasesPackage("com.itheima.domain"); // 设置数据源(自动注入Spring管理的DruidDataSource) ssfb.setDataSource(dataSource); return ssfb; } /** * 配置MapperScannerConfigurer,扫描Dao接口生成代理对象 */ @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); // 设置Dao接口的扫描包路径 msc.setBasePackage("com.itheima.dao"); return msc; } }重点说明:配置了MapperScannerConfigurer批量扫描dao包后,Mapper接口上无需添加@Mapper注解,即可被Spring管理并支持@Autowired注入。
1. @Mapper 注解
MyBatis 提供的注解,作用是标记当前接口为 Mapper 接口,让 Spring 能为该接口生成代理对象并注入到容器中。
2. @MapperScan / MapperScannerConfigurer
作用是批量扫描指定包下的所有接口,自动将其识别为 Mapper 接口,统一生成代理对象,无需逐个接口加注解。
3. 两者的使用关系
只要配置了批量扫描(@MapperScan / MapperScannerConfigurer),接口上就不需要再写 @Mapper;即使写了也不会报错,但属于冗余配置,没有实际作用。
4. 不同开发场景的使用规范
- 传统 SSM 框架:使用
MapperScannerConfigurer配置批量扫描,接口上不写@Mapper; - Spring Boot(主流方案):在启动类上添加
@MapperScan("xxx.dao")配置扫描包,接口上不写@Mapper; - 唯一必须写
@Mapper的场景:没有任何批量扫描配置,仅单独使用某个 Mapper 接口,这句话仅在 Spring Boot 中是可实现的。在 SSM 中不成立,因为 SSM 中即使给单个接口加 @Mapper,不配置扫描器也无法生效,不存在「不配置批量扫描就能单独用 @Mapper」的情况。
补充生效规则:
- 纯 Spring(SSM)环境:
@Mapper必须配合MapperScannerConfigurer才能生效; - Spring Boot 环境:
@Mapper可以直接生效,因为 Spring Boot 的 MyBatis 自动配置已经帮你完成了底层扫描逻辑。
2.5 完善主配置类
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class, MybatisConfig.class}) public class SpringConfig { }2.6 编写运行测试类
public class App2 { public static void main(String[] args) { // 启动Spring容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); // 获取Service Bean AccountService accountService = ctx.getBean(AccountService.class); // 执行查询 Account ac = accountService.findById(1); System.out.println(ac); } }2.7 项目结构说明
spring_15_spring_mybatis ├── src │ ├── main │ │ ├── java │ │ │ └── com.itheima │ │ │ ├── config │ │ │ │ ├── JdbcConfig.java │ │ │ │ ├── MybatisConfig.java │ │ │ │ └── SpringConfig.java │ │ │ ├── dao │ │ │ │ └── AccountDao.java │ │ │ ├── domain │ │ │ │ └── Account.java │ │ │ ├── service │ │ │ │ ├── AccountService.java │ │ │ │ └── impl │ │ │ │ └── AccountServiceImpl.java │ │ │ ├── App.java │ │ │ └── App2.java │ │ └── resources │ │ └── jdbc.properties └── pom.xml三、Spring整合Junit单元测试
3.1 环境准备
直接使用上面整合好的Spring+MyBatis项目,在test目录下编写测试类。
3.2 整合步骤与代码实现
步骤 1:引入依赖 在pom.xml中添加 Junit 和 Spring 测试依赖:<!-- Junit依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- Spring测试依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency>步骤 2:编写测试类 在test/java/com/itheima下创建AccountServiceTest测试类:// 设置运行器:Spring整合Junit的专用运行器 @RunWith(SpringJUnit4ClassRunner.class) // 设置Spring配置类 @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { // 自动注入Service Bean @Autowired private AccountService accountService; @Test public void testFindById(){ System.out.println(accountService.findById(1)); } @Test public void testFindAll(){ System.out.println(accountService.findAll()); } }关键注解说明:
@RunWith(SpringJUnit4ClassRunner.class):必须添加,Spring 提供的专用运行器,负责自动创建 Spring 容器。
@ContextConfiguration(classes = SpringConfig.class):指定 Spring 配置类,加载容器。
@Autowired:直接注入 Bean,无需手动获取。
3.3 整合vs手动方式对比
| 对比项 | 整合Spring+Junit(推荐) | 不整合(手动) |
|---|---|---|
| 容器创建 | 自动创建 | 手动new容器 |
| Bean获取 | @Autowired直接注入 | getBean获取 |
| 代码简洁性 | 极高 | 冗余 |
手动方式代码示例(不推荐):
public class AccountServiceTest { @Test public void testFindById() { // 手动创建Spring容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); // 获取Bean AccountService accountService = ctx.getBean(AccountService.class); // 执行测试 System.out.println(accountService.findById(1)); } }四、常见问题与注意事项
- SqlSessionFactoryBean的dataSource必须是Spring管理的,不能手动new。
- MapperScannerConfigurer的basePackage必须准确,否则无法生成Mapper代理对象。
- 配置MapperScannerConfigurer后,Mapper接口不需要@Mapper注解。
- @PropertySource必须加classpath:,确保配置文件加载成功。
- SqlSession线程不安全,不能交给Spring单例管理。
- 测试类中@RunWith必须在@ContextConfiguration之前。
五、总结
Spring整合MyBatis的核心思想是将MyBatis的核心对象交给Spring IoC容器统一管理,实现解耦、简化开发、统一配置:
- 用SqlSessionFactoryBean替代SqlMapConfig.xml,管理SqlSessionFactory;
- 用MapperScannerConfigurer自动扫描Dao接口,生成代理对象,无需@Mapper;
- 整合Junit后,自动创建容器,大幅简化单元测试。