Embedded Database Support

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

为什么使用嵌入式数据库?

Why Use an Embedded Database?

在项目的开发阶段,嵌入式数据库是非常有用的,因为它具有轻量级特性。它的好处包括易于配置,快速启动,可测试性,以及在开发过程中快速发展你的 SQL 的能力。

通过使用 Spring XML 创建一个嵌入式数据库

Creating an Embedded Database by Using Spring XML

如果你想把嵌入式数据库实例作为 Spring ApplicationContext 中的一个 Bean 公开,你可以使用 spring-jdbc 命名空间中的嵌入式数据库标签。

  1. <jdbc:embedded-database id="dataSource" generate-name="true">
  2. <jdbc:script location="classpath:schema.sql"/>
  3. <jdbc:script location="classpath:test-data.sql"/>
  4. </jdbc:embedded-database>

前面的配置创建了一个嵌入式 HSQL 数据库,该数据库由 classpath 根部的 schema.sql 和 test-data.sql 资源中的 SQL 填充。此外,作为一个最佳实践,嵌入式数据库被分配了一个唯一生成的名称。嵌入式数据库作为 javax.sql.DataSource类型的 bean 提供给 Spring 容器,然后可以根据需要注入到数据访问对象中。

以编程方式创建一个嵌入式数据库

Creating an Embedded Database Programmatically

EmbeddedDatabaseBuilder 类提供了一个流畅的 API,用于以编程方式构建一个嵌入式数据库。当你需要在一个独立的环境中或独立的集成测试中创建一个嵌入式数据库时,你可以使用它,就像下面的例子。

  1. EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
  2. .generateUniqueName(true)
  3. .setType(H2)
  4. .setScriptEncoding("UTF-8")
  5. .ignoreFailedDrops(true)
  6. .addScript("schema.sql")
  7. .addScripts("user_data.sql", "country_data.sql")
  8. .build();
  9. // 对数据库进行操作(EmbeddedDatabase 扩展了 javax.sql.DataSource)。
  10. // 所以可以将 db 传给 JdbcTemplate 构造函数使用,然后使用 JdbcTemplate 来执行数据库操作
  11. db.shutdown()

想要它生效,需要将 H2 的 jar 包依赖添加到项目中

  1. testImplementation group: 'com.h2database', name: 'h2', version: '2.1.210'

请看 EmbeddedDatabaseBuilder 的 javadoc 以了解所有支持的选项的进一步细节。

你也可以使用 EmbeddedDatabaseBuilder 通过使用 Java 配置来创建一个嵌入式数据库,如下面的例子所示:

  1. @Configuration
  2. public class DataSourceConfig {
  3. @Bean
  4. public DataSource dataSource() {
  5. return new EmbeddedDatabaseBuilder()
  6. .generateUniqueName(true)
  7. .setType(H2)
  8. .setScriptEncoding("UTF-8")
  9. .ignoreFailedDrops(true)
  10. .addScript("schema.sql")
  11. .addScripts("user_data.sql", "country_data.sql")
  12. .build();
  13. }
  14. }

:::tips 我不熟悉这个 H2 的语法,所以没法进行真实的测试 :::

选择嵌入式数据库类型

Selecting the Embedded Database Type

本节介绍了如何从 Spring 支持的三种嵌入式数据库中选择一种。它包括以下主题:

  • 使用 HSQL
  • 使用 H2
  • 使用 Derby

使用 HSQL

Spring 支持 HSQL 1.8.0 及以上版本。如果没有明确指定类型,HSQL 是默认的嵌入式数据库。要明确指定 HSQL,请将嵌入式数据库标签的类型属性设置为 HSQL。如果你使用构建器 API,请用 EmbeddedDatabaseType.HSQL 调用 setType(EmbeddedDatabaseType)方法。

使用 H2

Spring 支持 H2 数据库。要启用 H2,请将嵌入式数据库标签的类型属性设置为 H2。如果你使用构建器 API,用 EmbeddedDatabaseType.H2 调用 setType(EmbeddedDatabaseType)方法。

使用 Derby

Spring 支持 Apache Derby 10.5 及以上版本。要启用 Derby,请将嵌入式数据库标签的类型属性设置为 DERBY。如果你使用构建器 API,请用EmbeddedDatabaseType.DERBY 调用 setType(EmbeddedDatabaseType)方法。

用嵌入式数据库测试数据访问逻辑

Testing Data Access Logic with an Embedded Database

嵌入式数据库提供了一种测试数据访问代码的轻量级方式。下一个例子是一个使用嵌入式数据库的数据访问集成测试模板。当嵌入式数据库不需要在不同的测试类中重复使用时,使用这样的模板对一次性的测试很有用。然而,如果你想创建一个在测试套件中共享的嵌入式数据库,可以考虑使用 Spring TestContext 框架,并将嵌入式数据库配置为 Spring ApplicationContext 中的一个 Bean,正如在使用 Spring XML 创建嵌入式数据库和以编程方式创建嵌入式数据库中描述的那样。下面的列表显示了测试模板:

  1. public class DataAccessIntegrationTestTemplate {
  2. private EmbeddedDatabase db;
  3. @BeforeEach
  4. public void setUp() {
  5. // creates an HSQL in-memory database populated from default scripts
  6. // classpath:schema.sql and classpath:data.sql
  7. db = new EmbeddedDatabaseBuilder()
  8. .generateUniqueName(true)
  9. .addDefaultScripts()
  10. .build();
  11. }
  12. @Test
  13. public void testDataAccess() {
  14. JdbcTemplate template = new JdbcTemplate(db);
  15. template.query( /* ... */ );
  16. }
  17. @AfterEach
  18. public void tearDown() {
  19. db.shutdown();
  20. }
  21. }

为嵌入式数据库生成唯一的名称

Generating Unique Names for Embedded Databases

开发团队经常遇到嵌入式数据库的错误,如果他们的测试套件无意中试图重新创建同一数据库的额外实例。如果一个 XML 配置文件或 @Configuration 类负责创建嵌入式数据库,并且相应的配置随后在同一测试套件(即在同一 JVM 进程中)的多个测试场景中被重复使用,那么这种情况就很容易发生,例如,针对嵌入式数据库的集成测试,其 ApplicationContext 配置仅在哪些 bean 定义配置文件处于活动状态方面有所不同。

这类错误的根本原因是 Spring 的 EmbeddedDatabaseFactory(由 <jdbc:embedded-database> XML 命名空间元素和EmbeddedDatabaseBuilder for Java 配置在内部使用)将嵌入式数据库的名称设置为 testdb,如果没有另外指定。对于 <jdbc:embedded-database>的情况,嵌入式数据库通常被分配一个与 Bean 的 id 相等的名字(通常是类似 dataSource 的东西)。因此,随后创建嵌入式数据库的尝试不会导致一个新的数据库。相反,相同的 JDBC 连接 URL 被重复使用,创建新的嵌入式数据库的尝试实际上是指向从相同配置中创建的现有嵌入式数据库。

为了解决这个常见的问题,Spring Framework 4.2 提供了对生成嵌入式数据库的唯一名称的支持。要启用生成的名称,请使用以下选项之一:

  • EmbeddedDatabaseFactory.setGenerateUniqueDatabaseName()
  • EmbeddedDatabaseBuilder.generateUniqueName()
  • <jdbc:embedded-database generate-name="true" … >

扩展嵌入式数据库支持

Extending the Embedded Database Support

你可以通过两种方式扩展 Spring JDBC 的嵌入式数据库支持。

  • 实现 EmbeddedDatabaseConfigurer 来支持一个新的嵌入式数据库类型。
  • 实现 DataSourceFactory 以支持新的 DataSource 实现,例如管理嵌入式数据库连接的连接池。

我们鼓励你在 GitHub Issues 上为 Spring 社区贡献扩展。