使用内存数据库(如H2)进行MyBatis功能测试,能实现轻量、高效、隔离的测试环境(无需部署真实数据库,测试后数据自动销毁)。
一、核心优势
- 轻量无依赖:无需安装/部署数据库,随用随建;
- 速度快:内存操作,测试执行效率远高于真实数据库;
- 数据隔离:每个测试用例可独立初始化数据,无跨用例污染;
- 无残留:测试结束后内存释放,无需手动清理数据。
二、选型推荐
优先选择 H2数据库:
- 支持内存模式(
mem:)、文件模式; - 兼容MySQL/Oracle等主流数据库语法(通过
MODE参数); - 集成简单,与MyBatis/Spring Boot适配性好。
三、实操步骤(主流场景:Spring Boot + MyBatis)
1. 引入核心依赖(Maven)
<dependencies>
<!-- MyBatis + Spring Boot整合包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- H2内存数据库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot测试包(JUnit 5) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 配置H2数据源与MyBatis
在src/test/resources/application.yml中配置:
spring:
# H2数据源配置
datasource:
# 内存模式:testdb为数据库名;MODE=MySQL兼容MySQL语法;DB_CLOSE_DELAY=-1防止连接关闭后数据库销毁
url: jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa # 默认用户名
password: # 默认密码为空
# 开启H2控制台(可选,方便调试)
h2:
console:
enabled: true # 访问地址:http://localhost:8080/h2-console
path: /h2-console
# MyBatis配置
mybatis:
mapper-locations: classpath:mapper/*.xml # Mapper XML文件位置
type-aliases-package: com.example.demo.entity # 实体类别名包
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL日志(方便调试)
3. 初始化测试表和数据
Spring Boot会自动执行src/test/resources下的schema.sql(建表)和data.sql(插测试数据):
schema.sql(建表脚本):
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INT COMMENT '年龄'
);
data.sql(测试数据):
INSERT INTO user (name, age) VALUES ('张三', 20);
INSERT INTO user (name, age) VALUES ('李四', 25);
4. 编写核心代码(实体+Mapper)
(1)实体类 User.java
package com.example.demo.entity;
public class User {
private Long id;
private String name;
private Integer age;
// 省略getter/setter/toString
}
(2)Mapper接口 UserMapper.java
支持注解/XML两种方式,这里用注解示例:
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User getById(Long id);
@Select("SELECT * FROM user")
List<User> listAll();
@Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 自增主键回填
int insert(User user);
@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int delete(Long id);
}
5. 编写测试用例
核心:用@SpringBootTest启动Spring上下文,@Transactional保证测试后数据回滚(避免污染)。
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 启动Spring Boot上下文
@Transactional // 测试方法执行后自动回滚事务,数据不残留
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
// 测试查询单个
@Test
void getById() {
User user = userMapper.getById(1L);
assertNotNull(user);
assertEquals("张三", user.getName());
assertEquals(20, user.getAge());
}
// 测试查询全部
@Test
void listAll() {
List<User> users = userMapper.listAll();
assertEquals(2, users.size());
}
// 测试新增
@Test
void insert() {
User user = new User();
user.setName("王五");
user.setAge(30);
int rows = userMapper.insert(user);
assertEquals(1, rows); // 验证插入行数
assertNotNull(user.getId()); // 验证主键回填
assertEquals(3, user.getId()); // 自增ID:1、2已存在,新ID为3
}
// 测试更新
@Test
void update() {
User user = userMapper.getById(1L);
user.setName("张三更新");
user.setAge(21);
int rows = userMapper.update(user);
assertEquals(1, rows);
User updatedUser = userMapper.getById(1L);
assertEquals("张三更新", updatedUser.getName());
}
// 测试删除
@Test
void delete() {
int rows = userMapper.delete(1L);
assertEquals(1, rows);
assertNull(userMapper.getById(1L));
}
}
四、纯MyBatis场景(无Spring)
若未使用Spring,需手动管理SqlSession和数据库初始化:
1. 配置mybatis-config.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/> <!-- Mapper XML路径 -->
</mappers>
</configuration>
2. 编写测试用例
package com.example.test;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import static org.junit.jupiter.api.Assertions.*;
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
private UserMapper userMapper;
// 每个测试方法执行前初始化:加载配置+创建表+插数据
@BeforeEach
void setUp() throws IOException, Exception {
// 1. 加载MyBatis配置
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 手动初始化H2表和数据
try (Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", "")) {
Statement stmt = conn.createStatement();
stmt.execute("DROP TABLE IF EXISTS user;");
stmt.execute("CREATE TABLE user (id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), age INT);");
stmt.execute("INSERT INTO user (name, age) VALUES ('张三', 20), ('李四', 25);");
}
// 3. 获取SqlSession(自动提交事务)
sqlSession = sqlSessionFactory.openSession(true);
userMapper = sqlSession.getMapper(UserMapper.class);
}
// 每个测试方法执行后关闭SqlSession
@AfterEach
void tearDown() {
if (sqlSession != null) {
sqlSession.close();
}
}
@Test
void getById() {
User user = userMapper.getById(1L);
assertNotNull(user);
assertEquals("张三", user.getName());
}
}
五、关键注意事项
-
语法兼容:
H2默认语法与MySQL/Oracle有差异,需通过MODE参数适配:- MySQL:
jdbc:h2:mem:testdb;MODE=MySQL - Oracle:
jdbc:h2:mem:testdb;MODE=Oracle
- MySQL:
-
数据隔离:
- Spring场景:用
@Transactional回滚测试数据; - 纯MyBatis场景:每个测试方法重新初始化数据库(
@BeforeEach执行建表/插数据)。
- Spring场景:用
-
H2控制台调试:
启动应用后访问http://localhost:8080/h2-console,输入配置的JDBC URL(如jdbc:h2:mem:testdb),用户名sa,密码为空,即可查看测试数据。 -
日志调试:
开启MyBatis SQL日志(见application.yml配置),可直观看到执行的SQL和参数,快速定位问题。
六、总结
核心流程:
引入H2依赖 → 配置内存数据源 → 初始化测试表/数据 → 编写Mapper → 编写测试用例(验证CRUD)。
通过内存数据库,能快速搭建隔离的测试环境,确保MyBatis的SQL逻辑、映射关系正确,且测试执行效率极高。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接
文章评论