摘要: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 id="mysql">
<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 0
1.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后,自动创建容器,大幅简化单元测试。