数据库初始化

根据您所使用的技术栈不同,SQL 数据库可以通过多种方式进行初始化。 当然,只要数据库是独立运行的进程,您也可以手动完成初始化。 建议使用单一机制进行模式(schema)生成。spring-doc.cadn.net.cn

使用 Hibernate 初始化数据库

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

在从内存数据库切换到“真实”数据库时,请务必小心,不要对新平台中表和数据的存在做出假设。 你必须显式设置 ddl-auto,或者使用其他机制之一来初始化数据库。spring-doc.cadn.net.cn

你可以通过启用 org.hibernate.SQL 日志记录器来输出建表语句。 如果你启用了调试模式,系统会自动为你完成此操作。

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

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

Spring Boot 可以自动创建您的 JDBC DataSource 或 R2DBC ConnectionFactory 的模式(DDL 脚本),并初始化其数据(DML 脚本)。spring-doc.cadn.net.cn

默认情况下,它会从 optional:classpath*:schema.sql 加载 schema 脚本,从 optional:classpath*:data.sql 加载数据脚本。 这些 schema 和数据脚本的位置可以通过分别使用 spring.sql.init.schema-locationsspring.sql.init.data-locations 进行自定义。 optional: 前缀表示即使这些文件不存在,应用程序也会正常启动。 如果希望在文件缺失时让应用程序启动失败,请移除 optional: 前缀。spring-doc.cadn.net.cn

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

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

基于脚本的 DataSource 初始化默认在任何 JPA EntityManagerFactory Bean 创建之前执行。 schema.sql 可用于为 JPA 管理的实体创建模式,而 data.sql 可用于填充该模式。 虽然我们不推荐使用多种数据源初始化技术,但如果您希望基于脚本的 DataSource 初始化能够建立在 Hibernate 执行的模式创建之上,请将 spring.jpa.defer-datasource-initialization 设置为 true。 这将推迟数据源初始化,直到所有 EntityManagerFactory Bean 被创建并初始化之后。 随后可以使用 schema.sql 向 Hibernate 执行的任何模式创建添加内容,并使用 data.sql 填充该模式。spring-doc.cadn.net.cn

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

如果您正在使用更高级别的数据库迁移工具(例如 Flyway 或 Liquibase),应仅使用这些工具来创建和初始化数据库模式。 不建议将基础的 schema.sqldata.sql 脚本与 Flyway 或 Liquibase 同时使用,且在未来的版本中将移除对此类用法的支持。spring-doc.cadn.net.cn

如果您需要使用更高级的数据库迁移工具来初始化测试数据,请参阅关于FlywayLiquibase的部分。spring-doc.cadn.net.cn

初始化 Spring Batch 数据库

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

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

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

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

Spring Boot 支持两种高级迁移工具:FlywayLiquibasespring-doc.cadn.net.cn

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

要自动在启动时运行 Flyway 数据库迁移,请将相应的 Flyway 模块添加到您的类路径中。 内存数据库和基于文件的数据库由 org.flywaydb:flyway-core 支持。 否则,需要一个特定于数据库的模块。 例如,使用 PostgreSQL 时请使用 org.flywaydb:flyway-database-postgresql,使用 MySQL 时请使用 org.flywaydb:flyway-mysql。 更多详细信息,请参阅Flyway 官方文档spring-doc.cadn.net.cn

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

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

你还可以添加一个特殊的 {vendor} 占位符,以使用特定于提供商的脚本。 假设如下:spring-doc.cadn.net.cn

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

前述配置并未使用 db/migration,而是根据数据库类型设置要使用的目录(例如,对于 MySQL 设置为 db/migration/mysql)。 支持的数据库列表可在 DatabaseDriver 中查看。spring-doc.cadn.net.cn

迁移也可以用 Java 编写。 Flyway 将自动配置任何实现了 JavaMigration 的 Bean。spring-doc.cadn.net.cn

FlywayProperties 提供了 Flyway 的大部分设置,以及少量可用于禁用迁移或关闭位置检查的附加属性。 如果您需要更灵活地控制配置,请考虑注册一个 FlywayConfigurationCustomizer Bean。spring-doc.cadn.net.cn

Spring Boot 调用 Flyway.migrate() 来执行数据库迁移。 如果您希望获得更多控制权,请提供一个实现了 FlywayMigrationStrategy@Beanspring-doc.cadn.net.cn

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

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

你也可以使用 Flyway 为特定场景提供数据。 例如,你可以将测试专用的迁移脚本放在 src/test/resources 目录下,这些脚本仅在应用程序为测试而启动时才会执行。 此外,你还可以使用特定于配置文件(profile)的配置来自定义 spring.flyway.locations,以便仅在某个特定配置文件激活时才运行某些迁移脚本。 例如,在 application-dev.properties 文件中,你可以指定以下设置:spring-doc.cadn.net.cn

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

通过该配置,只有在 dev/db/migration 配置文件处于激活状态时,dev 中的迁移才会执行。spring-doc.cadn.net.cn

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

要自动在启动时运行 Liquibase 数据库迁移,请将 org.liquibase:liquibase-core 添加到您的类路径中。spring-doc.cadn.net.cn

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

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

默认情况下,Liquibase 会自动装配上下文中的 (@Primary) DataSource 并将其用于迁移。 如果您需要使用不同的 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 属性的值。spring-doc.cadn.net.cn

有关可用设置(如上下文、默认架构等)的详细信息,请参阅 LiquibasePropertiesspring-doc.cadn.net.cn

如果您希望在 Customizer<Liquibase> bean 被使用之前自定义 Liquibase 实例,也可以使用它。spring-doc.cadn.net.cn

使用 Flyway 进行仅测试用的迁移

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

使用 Liquibase 进行仅测试用的迁移

如果你想创建用于填充测试数据库的 Liquibase 迁移脚本,可以利用 Liquibase 上下文(contexts)。 另请参阅相关的 博客文章spring-doc.cadn.net.cn

实际上,这意味着需要在包含测试数据的变更集(changeset)中添加一个 context:@test 属性,例如:spring-doc.cadn.net.cn

--liquibase formatted sql

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

并在希望应用包含测试数据的变更集的环境中,使用 spring.liquibase.contexts=testspring-doc.cadn.net.cn

依赖于已初始化的数据库

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

检测数据库初始化器

Spring Boot 将自动检测以下类型的 Bean,这些 Bean 用于初始化 SQL 数据库:spring-doc.cadn.net.cn

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

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

Spring Boot 将自动检测以下类型的 Bean,这些 Bean 依赖于数据库初始化:spring-doc.cadn.net.cn

如果您使用的是第三方Starters数据访问库,它可能会提供检测器,从而自动检测其他类型的 Bean。 若要检测其他 Bean,请在 META-INF/spring.factories 中注册一个 DependsOnDatabaseInitializationDetector 的实现。 或者,使用 @DependsOnDatabaseInitialization 注解该 Bean 的类或其 @Bean 方法。spring-doc.cadn.net.cn