跳到主要内容

数据库初始化

DeepSeek V3 中英对照 Database Initialization

SQL 数据库可以根据你的技术栈以不同的方式进行初始化。当然,如果数据库是一个独立的进程,你也可以手动进行初始化。建议使用单一机制来生成数据库模式。

使用 Hibernate 初始化数据库

你可以设置 spring.jpa.hibernate.ddl-auto 来控制 Hibernate 的数据库初始化。支持的值有 nonevalidateupdatecreatecreate-drop。Spring Boot 会根据你是否使用嵌入式数据库为你选择一个默认值。嵌入式数据库通过查看 Connection 类型和 JDBC url 来识别。hsqldbh2derby 是嵌入式数据库,其他则不是。如果识别出嵌入式数据库且未检测到模式管理器(Flyway 或 Liquibase),ddl-auto 默认值为 create-drop。在所有其他情况下,它默认为 none

在从内存数据库切换到“真实”数据库时,请务必小心,不要假设新平台中已经存在表和数据。你需要显式设置 ddl-auto,或者使用其他机制来初始化数据库。

备注

你可以通过启用 org.hibernate.SQL 日志记录器来输出模式创建信息。如果你启用了调试模式,这将自动为你完成。

此外,如果 Hibernate 从头开始创建架构(即如果 ddl-auto 属性设置为 createcreate-drop),则在启动时会执行类路径根目录下名为 import.sql 的文件。这对于演示和测试可能很有用,但如果你不小心,这可能不是你在生产环境中希望放在类路径上的东西。这是 Hibernate 的一个功能(与 Spring 无关)。

使用基本 SQL 脚本初始化数据库

Spring Boot 可以自动创建你的 JDBC DataSource 或 R2DBC ConnectionFactory 的 schema(DDL 脚本),并初始化其数据(DML 脚本)。

默认情况下,它会从 optional:classpath*:schema.sql 加载模式脚本,并从 optional:classpath*:data.sql 加载数据脚本。这些模式和数据脚本的位置可以分别使用 spring.sql.init.schema-locationsspring.sql.init.data-locations 进行自定义。optional: 前缀意味着即使文件不存在,应用程序也会启动。如果希望在文件缺失时应用程序启动失败,可以移除 optional: 前缀。

此外,Spring Boot 会处理 optional:classpath*:schema-${platform}.sqloptional:classpath*:data-${platform}.sql 文件(如果存在),其中 ${platform}spring.sql.init.platform 的值。这允许你在必要时切换到特定数据库的脚本。例如,你可以选择将其设置为数据库的供应商名称(如 hsqldbh2oraclemysqlpostgresql 等)。

默认情况下,SQL 数据库初始化仅在使用的嵌入式内存数据库时执行。要始终初始化 SQL 数据库,无论其类型如何,请将 spring.sql.init.mode 设置为 always。同样,要禁用初始化,请将 spring.sql.init.mode 设置为 never。默认情况下,Spring Boot 启用了基于脚本的数据库初始化器的快速失败功能。这意味着,如果脚本引发异常,应用程序将无法启动。你可以通过设置 spring.sql.init.continue-on-error 来调整此行为。

基于脚本的 DataSource 初始化默认在任何 JPA EntityManagerFactory Bean 创建之前执行。schema.sql 可用于为 JPA 管理的实体创建架构,而 data.sql 可用于填充数据。虽然我们不建议使用多种数据源初始化技术,但如果你希望基于脚本的 DataSource 初始化能够在 Hibernate 执行的架构创建基础上进行,请将 spring.jpa.defer-datasource-initialization 设置为 true。这将推迟数据源初始化,直到所有 EntityManagerFactory Bean 创建并初始化完成。然后,schema.sql 可用于对 Hibernate 执行的架构创建进行补充,而 data.sql 可用于填充数据。

备注

初始化脚本支持使用 -- 进行单行注释,以及 /* */ 进行块注释。不支持其他注释格式。

如果你正在使用像 Flyway 或 Liquibase 这样的高级数据库迁移工具,你应该单独使用它们来创建和初始化数据库模式。不建议将基本的 schema.sqldata.sql 脚本与 Flyway 或 Liquibase 一起使用,并且这种支持将在未来的版本中被移除。

如果你需要使用更高级的数据库迁移工具来初始化测试数据,请参阅关于 FlywayLiquibase 的部分。

初始化 Spring Batch 数据库

如果你使用 Spring Batch,它已经预装了适用于大多数流行数据库平台的 SQL 初始化脚本。Spring Boot 可以检测你的数据库类型,并在启动时执行这些脚本。如果你使用的是嵌入式数据库,这一过程默认就会发生。你也可以为任何数据库类型启用此功能,如下例所示:

spring.batch.jdbc.initialize-schema=always
properties

你也可以通过将 spring.batch.jdbc.initialize-schema 设置为 never 来显式关闭初始化。

使用更高级的数据库迁移工具

Spring Boot 支持两种更高级的迁移工具:FlywayLiquibase

启动时执行 Flyway 数据库迁移

为了在启动时自动运行 Flyway 数据库迁移,请将相应的 Flyway 模块添加到你的类路径中。内存和基于文件的数据库由 org.flywaydb:flyway-core 支持。否则,需要使用特定于数据库的模块。例如,PostgreSQL 使用 org.flywaydb:flyway-database-postgresql,MySQL 使用 org.flywaydb:flyway-mysql。更多详情请参阅 Flyway 文档

通常,迁移脚本的命名格式为 V<VERSION>__<NAME>.sql(其中 <VERSION> 是由下划线分隔的版本号,例如 12_1)。默认情况下,这些脚本位于名为 classpath:db/migration 的目录中,但你可以通过设置 spring.flyway.locations 来修改该位置。这是一个以逗号分隔的列表,包含一个或多个 classpath:filesystem: 路径。例如,以下配置将同时在默认的 classpath 路径和 /opt/migration 目录中搜索脚本:

spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
properties

你也可以添加一个特殊的 {vendor} 占位符来使用特定供应商的脚本。假设如下:

spring.flyway.locations=classpath:db/migration/{vendor}
properties

与使用 db/migration 不同,前述配置根据数据库类型设置了要使用的目录(例如,对于 MySQL 使用 db/migration/mysql)。支持的数据库列表可以在 DatabaseDriver 中找到。

迁移也可以用 Java 编写。Flyway 会自动配置所有实现 JavaMigration 的 Bean。

FlywayProperties 提供了 Flyway 的大部分设置以及一小部分额外的属性,这些属性可以用来禁用迁移或关闭位置检查。如果你需要对配置进行更多控制,可以考虑注册一个 FlywayConfigurationCustomizer bean。

Spring Boot 调用 Flyway.migrate() 来执行数据库迁移。如果你希望获得更多的控制权,可以提供一个实现了 FlywayMigrationStrategy@Bean

Flyway 支持 SQL 和 Java 回调。要使用基于 SQL 的回调,请将回调脚本放置在 classpath:db/migration 目录中。要使用基于 Java 的回调,请创建一个或多个实现 Callback 的 bean。任何这样的 bean 都会自动注册到 Flyway 中。它们可以通过使用 @Order 或实现 Ordered 来进行排序。

默认情况下,Flyway 会自动装配上下文中的(@PrimaryDataSource 并用于迁移。如果你想使用不同的 DataSource,你可以创建一个并将其 @Bean 标记为 @FlywayDataSource。如果你这样做并且想要两个数据源(例如保留主要的自动配置的 DataSource),请记住将 @Bean 注解的 defaultCandidate 属性设置为 false。或者,你可以通过在外部属性中设置 spring.flyway.[url,user,password] 来使用 Flyway 原生的 DataSource。设置 spring.flyway.urlspring.flyway.user 中的任意一个都足以使 Flyway 使用它自己的 DataSource。如果这三个属性中的任何一个没有设置,则会使用其等效的 spring.datasource 属性的值。

你也可以使用 Flyway 来为特定场景提供数据。例如,你可以将测试专用的迁移文件放在 src/test/resources 中,这样它们只会在应用程序启动进行测试时运行。此外,你可以使用特定于配置文件的配置来定制 spring.flyway.locations,以便某些迁移仅在特定配置文件激活时运行。例如,在 application-dev.properties 中,你可以指定以下设置:

spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
properties

通过这样的设置,dev/db/migration 中的迁移只会在 dev 配置文件激活时运行。

启动时执行 Liquibase 数据库迁移

为了在启动时自动运行 Liquibase 数据库迁移,请将 org.liquibase:liquibase-core 添加到你的类路径中。

备注

当你将 org.liquibase:liquibase-core 添加到你的类路径时,数据库迁移默认会在应用程序启动时和测试运行前执行。这种行为可以通过使用 spring.liquibase.enabled 属性进行自定义,在 maintest 配置中设置不同的值。无法使用两种不同的方式初始化数据库(例如在应用程序启动时使用 Liquibase,在测试运行时使用 JPA)。

默认情况下,主变更日志从 db/changelog/db.changelog-master.yaml 读取,但你可以通过设置 spring.liquibase.change-log 来更改位置。除了 YAML,Liquibase 还支持 JSON、XML 和 SQL 变更日志格式。

默认情况下,Liquibase 会自动装配上下文中的(@PrimaryDataSource,并将其用于迁移。如果你需要使用不同的 DataSource,可以创建一个并将其 @Bean 标记为 @LiquibaseDataSource。如果你这样做并且想要两个数据源(例如保留主要的自动配置的 DataSource),请记得将 @Bean 注解的 defaultCandidate 属性设置为 false。或者,你可以通过在外部属性中设置 spring.liquibase.[driver-class-name,url,user,password] 来使用 Liquibase 原生的 DataSource。设置 spring.liquibase.urlspring.liquibase.user 中的任意一个就足以让 Liquibase 使用其自身的 DataSource。如果这三个属性中有任何一个未设置,将使用其等效的 spring.datasource 属性的值。

有关可用设置(如上下文、默认模式等)的详细信息,请参见 LiquibaseProperties

你还可以使用 Customizer<Liquibase> bean 来在使用之前自定义 Liquibase 实例。

使用 Flyway 进行测试专用迁移

如果你想创建 Flyway 迁移来填充你的测试数据库,请将它们放在 src/test/resources/db/migration 目录下。例如,一个名为 src/test/resources/db/migration/V9999__test-data.sql 的文件将在生产迁移之后执行,并且仅在你运行测试时执行。你可以使用这个文件来创建所需的测试数据。该文件不会被打包到你的 uber jar 或容器中。

使用 Liquibase 进行仅测试的迁移

如果你想创建用于填充测试数据库的 Liquibase 迁移,你需要创建一个测试变更日志,并且这个日志还需要包含生产环境的变更日志。

首先,你需要在运行测试时配置 Liquibase 使用不同的变更日志。一种方法是为 Spring Boot 创建一个 test 配置文件,并将 Liquibase 属性放在其中。为此,创建一个名为 src/test/resources/application-test.properties 的文件,并将以下属性放入其中:

spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yaml
properties

这将配置 Liquibase 在 test 配置文件中运行时使用不同的变更日志。

现在在 src/test/resources/db/changelog/db.changelog-test.yaml 创建变更日志文件:

databaseChangeLog:
- include:
file: classpath:/db/changelog/db.changelog-master.yaml
- changeSet:
runOrder: "last"
id: "test"
changes:
# Insert your changes here
yaml

此变更日志将在运行测试时使用,并且不会打包到您的 uber jar 或容器中。它包括生产变更日志,然后声明一个新的变更集,其 runOrder: last 设置指定它在所有生产变更集运行之后执行。您现在可以使用例如 insert 变更集 来插入数据,或使用 sql 变更集 直接执行 SQL。

最后一步是配置 Spring Boot 在运行测试时激活 test 配置文件。为此,你可以在带有 @SpringBootTest 注解的测试类上添加 @ActiveProfiles("test") 注解。

依赖于已初始化的数据库

数据库初始化在应用启动时执行,作为应用上下文刷新的一部分。为了允许在启动期间访问已初始化的数据库,系统会自动检测那些充当数据库初始化器的 bean 以及那些需要数据库已经初始化的 bean。依赖于数据库已初始化的 bean 会配置为依赖于那些初始化数据库的 bean。如果在启动期间,你的应用尝试访问数据库但数据库尚未初始化,你可以配置额外的检测,以发现那些初始化数据库并需要数据库已初始化的 bean。

检测数据库初始化器

Spring Boot 会自动检测以下类型的 bean 来初始化 SQL 数据库:

如果你正在使用第三方启动器来处理数据库初始化库,它可能会提供一个探测器,以便自动检测其他类型的 bean。要让其他 bean 被检测到,需要在 META-INF/spring.factories 中注册一个 DatabaseInitializerDetector 的实现。

检测依赖于数据库初始化的 Bean

Spring Boot 会自动检测以下依赖于数据库初始化的 bean 类型:

如果你使用的是第三方启动数据访问库,它可能会提供一个检测器,以便自动检测其他类型的 bean。要让其他 bean 被检测到,可以在 META-INF/spring.factories 中注册一个 DependsOnDatabaseInitializationDetector 的实现。或者,可以使用 @DependsOnDatabaseInitialization 注解来标记 bean 的类或其 @Bean 方法。