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 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的核心就是两件事

  1. Spring管理SqlSessionFactory:将MyBatis的核心工厂交给Spring IoC容器,统一管理,避免手动创建。
  2. 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));
    }
}

四、常见问题与注意事项

  1. SqlSessionFactoryBean的dataSource必须是Spring管理的,不能手动new。
  2. MapperScannerConfigurer的basePackage必须准确,否则无法生成Mapper代理对象。
  3. 配置MapperScannerConfigurer后,Mapper接口不需要@Mapper注解。
  4. @PropertySource必须加classpath:,确保配置文件加载成功。
  5. SqlSession线程不安全,不能交给Spring单例管理。
  6. 测试类中@RunWith必须在@ContextConfiguration之前。

五、总结

Spring整合MyBatis的核心思想是将MyBatis的核心对象交给Spring IoC容器统一管理,实现解耦、简化开发、统一配置:

  • 用SqlSessionFactoryBean替代SqlMapConfig.xml,管理SqlSessionFactory;
  • 用MapperScannerConfigurer自动扫描Dao接口,生成代理对象,无需@Mapper
  • 整合Junit后,自动创建容器,大幅简化单元测试。
相关推荐
咖啡八杯15 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
Flittly2 天前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
考虑考虑3 天前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯3 天前
GoF设计模式——中介者模式
java·后端·spring·设计模式
Flittly4 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
唐青枫8 天前
Java Spring WebFlux 实战指南:用 Mono、Flux 和 WebClient 写响应式接口
java·spring
咖啡八杯10 天前
GoF设计模式——策略模式
java·后端·spring·设计模式
Flittly11 天前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
dunky11 天前
Spring 的三级缓存与循环依赖
后端·spring