Skip to content

内嵌数据库支持 (Embedded Database Support)

org.springframework.jdbc.datasource.embedded 包提供了对内嵌 Java 数据库引擎的支持。原生支持 HSQLH2Derby。你也可以使用可扩展的 API 来插入新的内嵌数据库类型和 DataSource 实现。

为什么使用内嵌数据库?

内嵌数据库在项目的开发阶段非常有用,因为它们具有轻量级的特性。其优点包括配置简单、启动速度快、易于测试,以及在开发过程中能快速演化 SQL 脚本。

创建内嵌数据库

你可以将内嵌数据库实例暴露为 Spring Bean,如下例所示:

java
@Bean
DataSource dataSource() {
	return new EmbeddedDatabaseBuilder()
			.generateUniqueName(true)
			.setType(EmbeddedDatabaseType.H2)
			.addScripts("schema.sql", "test-data.sql")
			.build();
}
kotlin
@Bean
fun dataSource() = EmbeddedDatabaseBuilder()
	.generateUniqueName(true)
	.setType(EmbeddedDatabaseType.H2)
	.addScripts("schema.sql", "test-data.sql")
	.build()
xml
<jdbc:embedded-database id="dataSource" generate-name="true" type="H2">
	<jdbc:script location="classpath:schema.sql"/>
	<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

上述配置创建了一个内嵌的 H2 数据库,并使用类路径根目录下的 schema.sqltest-data.sql 资源进行初始化。此外,作为最佳实践,内嵌数据库被分配了一个唯一生成的名称。该内嵌数据库以 javax.sql.DataSource 类型的 Bean 形式提供给 Spring 容器。

选择内嵌数据库类型

Spring 支持以下三种内嵌数据库:

  • HSQL: Spring 支持 HSQL 1.8.0 及以上版本。如果没有显式指定类型,HSQL 是默认的内嵌数据库。
  • H2: Spring 支持 H2 数据库。
  • Derby: Spring 支持 Apache Derby 10.5 及以上版本。

生成唯一名称

开发团队在使用内嵌数据库时经常会遇到错误,原因是测试套件无意中尝试重新创建相同数据库的多个实例。如果多个测试场景复用了相同的配置(例如在同一个 JVM 进程中运行),Spring 内部默认会将数据库命名为 testdb

为了解决这个问题,Spring 提供了生成唯一名称的支持:

  • Java 控制:.generateUniqueName(true)
  • XML 控制:generate-name="true"

使用内嵌数据库测试数据访问逻辑

内嵌数据库为测试数据访问代码提供了一种轻量级方案。

java
public class DataAccessIntegrationTestTemplate {

	private EmbeddedDatabase db;

	@BeforeEach
	public void setUp() {
		// 创建一个唯一的 HSQL 内存数据库,并加载默认脚本
		db = new EmbeddedDatabaseBuilder()
				.generateUniqueName(true)
				.addDefaultScripts()
				.build();
	}

	@Test
	public void testDataAccess() {
		JdbcTemplate template = new JdbcTemplate(db);
		template.query( /* ... */ );
	}

	@AfterEach
	public void tearDown() {
		db.shutdown();
	}
}

补充教学

1. 为什么 H2 是目前最受欢迎的选择?

虽然 Spring 支持 HSQL 和 Derby,但 H2 在现代开发中几乎成为了标准。原因如下:

  • 兼容模式:H2 拥有非常强大的模式模拟功能,可以模拟 MySQL, Oracle, PostgreSQL 等主流数据库的行为。这让你可以用“内嵌数据库”跑“生产级数据库”的方言。
  • 控制台交互:H2 提供了一个基于浏览器的 Web 控制台,你可以在测试运行期间打开浏览器查看内存里的数据,调试非常方便。
  • 性能:H2 的执行速度通常优于 HSQL。

2. generateUniqueName(true) 的生存法则

在运行单元测试时,Spring 的测试上下文缓存机制(Context Caching)虽然加快了测试速度,但也带来了副作用:如果你在多个配置文件里都定义了同一个名字的内嵌数据库,由于它们是在同一个 JVM 里运行的,第二个测试可能会尝试修改第一个测试正在使用的数据库,或者因为数据库已存在而报错。 始终开启唯一名称命名是一个好习惯,它能确保每个 ApplicationContext 都有自己独立的数据库“沙盒”。

3. 不要用内嵌数据库做性能压力测试

请记住,内嵌数据库(尤其是内存模式)与生产环境的传统磁盘数据库(如 MySQL, Oracle)在性能特征上完全不同:

  • 内存数据库没有磁盘 I/O 瓶颈。
  • 内存数据库通常是单进程的,没有复杂的网络开销。 如果你的 SQL 在 H2 里跑得很快,并不代表在生产环境里也很快。性能优化的验证必须在真实的数据库环境中进行。

Based on Spring Framework.