数据库初始化

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 的模式(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 来调整该行为。

默认情况下,在创建任何 JPA EntityManagerFactory bean 之前执行基于脚本的 DataSource 初始化。 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 可以检测您的数据库类型并在启动时执行这些脚本。 如果您使用嵌入式数据库,这是默认行为。 您也可以为任何数据库类型启用它,如下例所示:

  • Properties

  • YAML

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

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

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

Spring Boot 支持两个更高级别的迁移工具:https://flywaydb.org/[Flyway] 和 Liquibase

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

要在启动时自动运行 Flyway 数据库迁移,请将适当的 Flyway 模块添加到您的类路径。 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: 位置的逗号分隔列表。 例如,以下配置将在默认类路径位置和 /opt/migration 目录中搜索脚本:

  • Properties

  • YAML

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

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

  • Properties

  • YAML

spring.flyway.locations=classpath:db/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 回调。 要使用基于 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 中,您可能指定以下设置:

  • Properties

  • YAML

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

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

在启动时执行 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

如果您想在使用 Liquibase 实例之前自定义它,您也可以使用 Customizer<Liquibase> bean。

使用 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 的文件,并在其中放置以下属性:

  • Properties

  • YAML

spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yaml
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 变更集 来插入数据或 sql 变更集 来直接执行 SQL。

最后要做的是配置 Spring Boot 在运行测试时激活 test 配置文件。 为此,您可以在 @SpringBootTest 注释的测试类上添加 @ActiveProfiles("test") 注释。

依赖于初始化的数据库

数据库初始化在应用程序启动期间作为应用程序上下文刷新的一部分执行。 要允许在启动期间访问已初始化的数据库,会自动检测作为数据库初始化器的 bean 和需要该数据库已初始化的 bean。 需要数据库已初始化的 bean 被配置为依赖于初始化它的那些 bean。 如果在启动期间,您的应用程序尝试访问数据库但它尚未初始化,您可以配置额外检测初始化数据库和需要数据库已初始化的 bean。

检测数据库初始化器

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

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

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

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

如果您使用第三方 starter 数据访问库,它可能提供检测器,以便其他类型的 bean 也会被自动检测。 要使其他 bean 被检测,请在 META-INF/spring.factories 中注册 DependsOnDatabaseInitializationDetector 的实现。 或者,使用 @DependsOnDatabaseInitialization 注释 bean 的类或其 @Bean 方法。