1. 概述
在Spring Boot应用开发中,特别是在集成测试阶段,使用H2内存数据库能提供轻量级、高效的数据库交互模拟方案。作为开发者,我们经常需要在测试过程中初始化数据库结构、预加载数据或执行自定义SQL脚本。
本文将介绍在Spring Boot测试环境中使用H2数据库执行SQL脚本的几种常见方法,帮助大家高效管理测试数据。
2. 通过JDBC URL指定脚本
H2的一个实用特性是允许在数据库初始化时直接通过JDBC URL自动执行SQL脚本。下面通过示例说明具体用法。
首先,**在resources/sql
目录下创建SQL文件init_my_db.sql
**:
CREATE TABLE TASK_TABLE
(
ID INT PRIMARY KEY,
NAME VARCHAR(255)
);
INSERT INTO TASK_TABLE (ID, NAME) VALUES (1, '启动应用');
INSERT INTO TASK_TABLE (ID, NAME) VALUES (2, '检查数据表是否填充');
该脚本创建表并插入两条记录。接下来在Spring Boot应用中配置H2自动执行此脚本:
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:demodb;INIT=RUNSCRIPT FROM 'classpath:/sql/init_my_db.sql'
username: sa
password:
关键点在于在JDBC URL中添加INIT
子句:
INIT=RUNSCRIPT FROM 'classpath:/sql/init_my_db.sql'
RUNSCRIPT FROM <script_path>
是H2的专用命令,用于执行指定脚本。上述配置会在内存数据库启动时自动执行resources/sql/init_my_db.sql
,非常适合初始化测试数据。
验证脚本是否执行的测试代码:
List<String> expectedTaskNames = List.of("启动应用", "检查数据表是否填充");
List<String> taskNames = entityManager.createNativeQuery("SELECT NAME FROM TASK_TABLE ORDER BY ID")
.getResultStream()
.map(Object::toString)
.toList();
assertEquals(expectedTaskNames, taskNames);
测试通过即证明脚本已执行。注意:
- ✅
RUNSCRIPT
命令支持Java类路径(如classpath:/sql/...
) - ✅ 使用
file:
前缀可指定绝对路径(如RUNSCRIPT FROM 'file:/opt/scripts/init.sql'
)
3. Spring Boot内置脚本检测:schema.sql和data.sql
除了H2原生方案,当使用Spring Data JPA时,Spring Boot会自动检测并执行两个特殊文件:
schema.sql
:定义数据库结构(表、视图等)data.sql
:填充初始数据
无需修改JDBC URL,只需将SQL语句放入对应文件即可。下面通过示例说明:
首先创建resources/schema.sql
:
CREATE TABLE CITY
(
ID INT PRIMARY KEY ,
NAME VARCHAR(255)
);
然后创建resources/data.sql
:
INSERT INTO CITY (ID, NAME) VALUES (1, '纽约');
INSERT INTO CITY (ID, NAME) VALUES (2, '汉堡');
INSERT INTO CITY (ID, NAME) VALUES (3, '上海');
⚠️ 执行顺序固定:Spring Boot先执行schema.sql
,再执行data.sql
。
验证自动执行的测试代码:
List<String> expectedCityNames = List.of("纽约", "汉堡", "上海");
List<String> cityNames = entityManager.createNativeQuery("SELECT NAME FROM CITY ORDER BY ID")
.getResultStream()
.map(Object::toString)
.toList();
assertEquals(expectedCityNames, cityNames);
高级配置
如果需要自定义文件位置,可通过以下配置实现:
spring:
sql:
init:
schema-locations: classpath:/custom/schema.sql
data-locations: classpath:/custom/data.sql
❌ 禁用自动检测(必要时):
spring:
sql:
init:
mode: never
4. 通过EntityManager动态执行脚本
有时需要程序化执行SQL脚本(如重置测试数据),可通过原生查询调用H2的RUNSCRIPT
命令实现。
复用前面的CITY
表,创建resources/sql/add_cities.sql
:
INSERT INTO CITY (ID, NAME) VALUES (4, '巴黎');
INSERT INTO CITY (ID, NAME) VALUES (5, '柏林');
INSERT INTO CITY (ID, NAME) VALUES (6, '东京');
动态执行脚本的测试代码:
entityManager.createNativeQuery("RUNSCRIPT FROM 'classpath:/sql/add_cities.sql'")
.executeUpdate();
List<String> expectedCityNames = List.of("纽约", "汉堡", "上海", "巴黎", "柏林", "东京");
List<String> cityNames = entityManager.createNativeQuery("SELECT NAME FROM CITY ORDER BY ID")
.getResultStream()
.map(Object::toString)
.toList();
assertEquals(expectedCityNames, cityNames);
⚠️ 关键注意事项:
- 必须调用
executeUpdate()
执行脚本 - ❌ 即使脚本包含
SELECT
语句,调用getResultList()
也会抛出异常
5. 总结
本文介绍了在H2数据库中执行SQL脚本的三种核心方法:
- JDBC URL初始化:通过
INIT=RUNSCRIPT
实现数据库启动时自动执行 - Spring Boot自动检测:利用
schema.sql
和data.sql
简化配置 - 动态执行:通过
EntityManager
实现程序化脚本调用
每种方法适用于不同场景:
- ✅ JDBC URL方案适合简单初始化
- ✅ Spring Boot内置方案适合标准JPA项目
- ✅ 动态执行适合需要灵活控制的测试场景
合理运用这些策略,能显著提升集成测试的维护性和应用健壮性。完整示例代码可在GitHub仓库获取。