跳到主要内容
版本:3.5.10

Database Initialization

QWen Max 中英对照 Database Initialization

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

使用 Hibernate 初始化数据库

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

从内存数据库切换到“真实”数据库时,请注意不要对新平台中表和数据的存在做任何假设。你必须显式设置 ddl-auto,或者使用其他机制来初始化数据库。

备注

你可以通过启用 org.hibernate.SQL 日志记录器来输出 schema 创建语句。如果你启用了 debug 模式,该操作会自动为你完成。

此外,如果 Hibernate 从头开始创建 schema(即 ddl-auto 属性被设置为 createcreate-drop),那么在类路径根目录下名为 import.sql 的文件会在启动时被执行。如果你谨慎使用,这在演示和测试时可能很有用,但在生产环境中,你可能不希望该文件出现在类路径中。这是 Hibernate 的一个特性(与 Spring 无关)。

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

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

默认情况下,它会从 optional:classpath*:schema.sql 加载 schema 脚本,从 optional:classpath*:data.sql 加载数据脚本。这些 schema 和数据脚本的位置可以通过 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 启用了基于脚本的数据库初始化器的 fail-fast 特性。这意味着,如果脚本引发异常,应用程序将无法启动。你可以通过设置 spring.sql.init.continue-on-error 来调整该行为。

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

备注

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

如果你正在使用 更高级的数据库迁移工具,例如 Flyway 或 Liquibase,你应该仅使用这些工具来创建和初始化数据库 schema。不建议将基础的 schema.sqldata.sql 脚本与 Flyway 或 Liquibase 一起使用,并且在未来的版本中将移除对此的支持。

如果你需要使用更高层次的数据库迁移工具来初始化测试数据,请参阅关于 FlywayLiquibase 的相关章节。

初始化 Spring Batch 数据库

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

spring.batch.jdbc.initialize-schema=always

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

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

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

在启动时执行 Flyway 数据库迁移

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

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

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

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

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

前面的配置没有使用 db/migration,而是根据数据库类型(例如 MySQL 使用 db/migration/mysql)来设置要使用的目录。支持的数据库列表可在 DatabaseDriver 中找到。

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

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

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

Flyway 支持 SQL 和 Java callbacks。要使用基于 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 目录下,这些脚本仅在应用程序为测试目的启动时运行。此外,你还可以使用特定于 profile 的配置来自定义 spring.flyway.locations,以便某些迁移脚本仅在特定 profile 处于激活状态时运行。例如,在 application-dev.properties 中,你可以指定以下设置:

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

通过该设置,dev/db/migration 中的迁移仅在 dev 配置文件处于激活状态时运行。

在启动时执行 Liquibase 数据库迁移

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

备注

当你将 org.liquibase:liquibase-core 添加到 classpath 中时,数据库迁移默认会在应用程序启动期间以及测试运行之前自动执行。此行为可以通过 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。如果这三个属性(driver-class-nameurluserpassword)中有任何一个未设置,将使用其对应的 spring.datasource 属性值。

有关可用设置(例如 contexts、默认 schema 等)的详细信息,请参见 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 contexts。另请参阅相关的 博客文章

实际上,这意味着要在包含测试数据的 changeset 中添加一个 context:@test 属性,例如:

--liquibase formatted sql

--changeset alice:1 context:@test
insert into project (id, name) values (1, 'Spring Boot');

并在希望应用包含测试数据的变更集的环境中,使用 spring.liquibase.contexts=test

依赖一个已初始化的数据库

数据库初始化是在应用程序启动期间作为应用上下文刷新的一部分执行的。为了允许在启动过程中访问已初始化的数据库,系统会自动检测充当数据库初始化器的 Bean 以及那些需要数据库已完成初始化的 Bean。那些依赖于数据库已初始化的 Bean 会被配置为依赖于执行初始化的 Bean。如果在启动过程中,你的应用程序尝试访问数据库但该数据库尚未初始化,你可以配置额外的检测机制,以识别负责初始化数据库的 Bean 以及那些要求数据库必须已初始化的 Bean。

检测数据库初始化器

Spring Boot 会自动检测以下类型的 Bean,这些 Bean 用于初始化 SQL 数据库:

如果你正在使用某个数据库初始化库的第三方 starter,它可能会提供一个检测器,从而自动检测其他类型的 Bean。要使其他 Bean 被自动检测到,请在 META-INF/spring.factories 中注册一个 DatabaseInitializerDetector 的实现。

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

Spring Boot 将自动检测以下类型的 Bean,这些 Bean 依赖于数据库初始化:

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