Database Initialization

可以根据你的堆栈以不同的方式初始化 SQL 数据库。当然,你也可以手动执行此操作,前提是数据库是一个独立的进程。建议使用单个机制来生成 schema。

Initialize a Database Using JPA

JPA 具有 DDL 生成功能,这些功能可以设置在启动时针对数据库运行。这通过两个外部属性进行控制:

  • spring.jpa.generate-ddl(布尔值)开启和关闭该功能,并且它是与供应商无关的。

  • spring.jpa.hibernate.ddl-auto(枚举)是一个 Hibernate 功能,它以更精细的方式控制行为。本指南后面会更详细地描述此功能。

Initialize a Database Using Hibernate

你可以将 spring.jpa.hibernate.ddl-auto 显式设置为标准 Hibernate 属性值之一,这些值包括 nonevalidateupdatecreatecreate-drop。Spring Boot 会基于它认为什么是你的嵌入式数据库,为你选择一个默认值。如果没有检测到 schema 管理器,则默认为 create-drop,否则在所有其他情况下默认为 none。可以通过查看 Connection 类型和 JDBC url 检测到嵌入式数据库。hsqldbh2derby 是应选对象,而其他不是。在从内存中切换到“真实”数据库时要小心,不要对新平台中表和数据的存在做出假设。你必须显式设置 ddl-auto 或使用其他机制之一来初始化数据库。

你可以通过启用 org.hibernate.SQL 记录器来输出 schema 创建。如果你启用 debug mode,则会自动为你执行此操作。

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

Initialize a Database Using Basic SQL Scripts

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

默认情况下,它从 optional:classpath*:schema.sql 加载 schema 脚本,从 optional:classpath*:data.sql 加载数据脚本。这些 schema 和数据脚本的位置可以使用 configprop:spring.sql.init.schema-locations[] 和 configprop:spring.sql.init.data-locations[] 分别进行自定义。optional: 前缀意味着,即使文件不存在,应用程序也会启动。为了让应用程序在文件不存在时无法启动,请删除 optional: 前缀。

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

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

基于脚本的 DataSource 初始化默认在创建任何 JPA EntityManagerFactory bean 之前执行。schema.sql 可用于为 JPA 管理的实体创建架构,data.sql 可用于填充它。虽然我们不建议使用多个数据源初始化技术,但是如果您希望基于脚本的 DataSource 初始化能够建立在 Hibernate 执行的架构创建上,请将 configprop:spring.jpa.defer-datasource-initialization[] 设置为 true。这会将数据源初始化延迟到创建和初始化所有 EntityManagerFactory bean 之后。schema.sql 随后可用于对 Hibernate 执行的架构创建进行添加,data.sql 可用于填充它。

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

如果您正在使用 Higher-level Database Migration Tool 比如 Flyway 或 Liquibase,您应该单独使用它们来创建和初始化架构。不建议在使用了 Flyway 或 Liquibase 的情况下同时使用基本 schema.sqldata.sql 脚本,并且这种支持将在之后的版本中删除。

如果您需要使用更高级别的数据库迁移工具初始化测试数据,请参阅有关 FlywayLiquibase 的章节。

Initialize a Spring Batch Database

如果您使用 Spring Batch,它会预先打包 SQL 初始化脚本,以适用于大多数流行的数据库平台。Spring Boot 可以检测您的数据库类型并在启动时执行这些脚本。如果您使用嵌入式数据库,则默认即会执行该操作。您也可以如以下示例所示,针对任何数据库类型启用该操作:

spring:
  batch:
    jdbc:
      initialize-schema: "always"

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

Use a Higher-level Database Migration Tool

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

Execute Flyway Database Migrations on Startup

若要在启动时自动运行 Flyway 数据库迁移,将 org.flywaydb:flyway-core 添加到您的 classpath 中。

通常,迁移是 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)。受支持数据库的列表在 {code-spring-boot-src}/jdbc/DatabaseDriver.java[DatabaseDriver] 中。

迁移还可以用 Java 编写。Flyway 会自动配置任何实现 JavaMigration 的 bean。

{code-spring-boot-autoconfigure-src}/flyway/FlywayProperties.java[FlywayProperties] 提供了 Flyway 的大多数设置和一组附加属性,这些属性可用于禁用迁移或关闭位置检查。如果您需要更好地控制配置,请考虑注册 FlywayConfigurationCustomizer bean。

Spring Boot 调用 Flyway.migrate() 来执行数据库迁移。如果您需要更好地控制,请提供 {code-spring-boot-autoconfigure-src}/flyway/FlywayMigrationStrategy.java[FlywayMigrationStrategy] 中实现 @Bean 的 bean。

Flyway 支持 SQL 和 Java callbacks。若要使用基于 SQL 的回调,将回调脚本放在 classpath:db/migration 目录中。若要使用基于 Java 的回调,创建一个或多个 bean 来实现 Callback。任何这样的 bean 都将自动使用 Flyway 注册。您可以使用 @Order 或实现 Ordered 来对它们进行排序。还可以检测实现已弃用的 FlywayCallback 接口的 bean,但它们不能与 Callback bean 一起使用。

默认情况下,Flyway 会自动装配您上下文中的 (@Primary) DataSource 并将其用于迁移。如果您希望使用其他 DataSource,您可以创建一个 DataSource 并且将它的 @Bean 标记为 @FlywayDataSource。如果您这样做了并且想要两个数据源,请记住再创建一个数据源,并将其标记为 @Primary。或者,您可以通过在外部属性中设置 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"

使用此设置,仅在 dev 配置文件处于活动状态时,dev/db/migration 中的迁移才会运行。

Execute Liquibase Database Migrations on Startup

若要自动在启动时运行 Liquibase 数据库变更,请将 org.liquibase:liquibase-core 添加到你的类路径。

org.liquibase:liquibase-core 添加到类路径后,数据库变更在以下两种情况下默认运行:在应用程序启动期间在测试运行之前。此行为可以通过配置属性 configprop:spring.liquibase.enabled[] 进行自定义,设置 maintest 配置中的不同值。无法使用两种不同方法初始化数据库(例如,在应用程序启动时使用 Liquibase,在测试运行时使用 JPA)。

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

默认情况下,Liquibase 在上下文中自动装配 (@Primary) DataSource 并将其用于变更。如果你需要使用其他 DataSource,则可以创建它并将其 @Bean 标记为 @LiquibaseDataSource。如果你这样做并希望两个数据源,请务必另创建一个并将其标记为 @Primary。或者,你可以通过在外部属性中设置 spring.liquibase.[driver-class-name,url,user,password] 来使用 Liquibase 的原生 DataSource。设置 spring.liquibase.urlspring.liquibase.user 中的任何一个都足以使 Liquibase 使用其自己的 DataSource。如果没有设置这三个属性中的任何一个,则将使用其等效 spring.datasource 属性的值。

请参阅 {code-spring-boot-autoconfigure-src}/liquibase/LiquibaseProperties.java[LiquibaseProperties] 了解有关可用设置的详细信息,如上下文、默认架构等。

Use Flyway for test-only migrations

如果你希望创建为测试数据库填充数据的 Flyway 变更,请将它们放在 src/test/resources/db/migration 中。例如,名为 src/test/resources/db/migration/V9999__test-data.sql 的文件将在生产变更后执行,并且仅在运行测试时执行。你可以使用此文件创建所需的测试数据。此文件不会打包在 uber jar 或容器中。

Use Liquibase for test-only migrations

如果你希望创建为测试数据库填充数据的 Liquibase 迁移,则必须创建一个也包含产品变更日志的测试变更日志。

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

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

这将配置 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

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

最后要做的事情是在运行测试时配置 Spring Boot 以激活 test 概要文件。要执行此操作,你可以将 @ActiveProfiles("test") 批注添加到带批注的 @SpringBootTest 测试类。

Depend Upon an Initialized Database

数据库初始化在应用程序启动期间作为应用程序上下文刷新的部分执行。为了允许在启动期间访问已初始化的数据库,检测作为数据库初始化程序的 bean 以及需要初始化数据库的 bean。依赖于已初始化数据库才能初始化的 bean 被配置为依赖于初始化数据库的 bean。如果在启动期间,你的应用程序尝试访问数据库但尚未初始化,则可以配置对初始化数据库和需要数据库已初始化的 bean 的附加检测。

Detect a Database Initializer

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

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

如果你正在为数据库初始化库使用第三方启动程序,它可能会提供检测器,以便也能自动检测其他类型的 bean。为了检测其他 bean,在 META-INF/spring.factories 中注册 DatabaseInitializerDetector 的实现。

Detect a Bean That Depends On Database Initialization

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

  • AbstractEntityManagerFactoryBean(除非 configprop:spring.jpa.defer-datasource-initialization[] 设置为 true

  • DSLContext (jOOQ)

  • EntityManagerFactory(除非 configprop:spring.jpa.defer-datasource-initialization[] 设置为 true

  • JdbcClient

  • JdbcOperations

  • NamedParameterJdbcOperations

如果你正在使用第三方启动程序数据访问库,它可能会提供检测器,以便也能自动检测其他类型的 bean。为了检测其他 bean,在 META-INF/spring.factories 中注册 DependsOnDatabaseInitializationDetector 的实现。或者,使用 @DependsOnDatabaseInitialization 批注 bean 的类或其 @Bean 方法。