Database Initialization

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

An SQL database can be initialized in different ways depending on what your stack is. Of course, you can also do it manually, provided the database is a separate process. It is recommended to use a single mechanism for schema generation.

Initialize a Database Using JPA

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

JPA has features for DDL generation, and these can be set up to run on startup against the database. This is controlled through two external properties:

  • spring.jpa.generate-ddl (boolean) switches the feature on and off and is vendor independent.

  • spring.jpa.hibernate.ddl-auto (enum) is a Hibernate feature that controls the behavior in a more fine-grained way. This feature is described in more detail later in this guide.

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 或使用其他机制之一来初始化数据库。

You can set spring.jpa.hibernate.ddl-auto explicitly to one of the standard Hibernate property values which are none, validate, update, create, and create-drop. Spring Boot chooses a default value for you based on whether it thinks your database is embedded. It defaults to create-drop if no schema manager has been detected or none in all other cases. An embedded database is detected by looking at the Connection type and JDBC url. hsqldb, h2, and derby are candidates, while others are not. Be careful when switching from in-memory to a '`real’ database that you do not make assumptions about the existence of the tables and data in the new platform. You either have to set ddl-auto explicitly or use one of the other mechanisms to initialize the database.

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

You can output the schema creation by enabling the org.hibernate.SQL logger. This is done for you automatically if you enable the debug mode.

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

In addition, a file named import.sql in the root of the classpath is executed on startup if Hibernate creates the schema from scratch (that is, if the ddl-auto property is set to create or create-drop). This can be useful for demos and for testing if you are careful but is probably not something you want to be on the classpath in production. It is a Hibernate feature (and has nothing to do with Spring).

Initialize a Database Using Basic SQL Scripts

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

Spring Boot can automatically create the schema (DDL scripts) of your JDBC DataSource or R2DBC ConnectionFactory and initialize its data (DML scripts).

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

By default, it loads schema scripts from optional:classpath*:schema.sql and data scripts from optional:classpath*:data.sql. The locations of these schema and data scripts can be customized using configprop:spring.sql.init.schema-locations[] and configprop:spring.sql.init.data-locations[] respectively. The optional: prefix means that the application will start even when the files do not exist. To have the application fail to start when the files are absent, remove the optional: prefix.

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

In addition, Spring Boot processes the optional:classpath*:schema-${platform}.sql and optional:classpath*:data-${platform}.sql files (if present), where ${platform} is the value of configprop:spring.sql.init.platform[]. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).

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

By default, SQL database initialization is only performed when using an embedded in-memory database. To always initialize an SQL database, irrespective of its type, set configprop:spring.sql.init.mode[] to always. Similarly, to disable initialization, set configprop:spring.sql.init.mode[] to never. By default, Spring Boot enables the fail-fast feature of its script-based database initializer. This means that, if the scripts cause exceptions, the application fails to start. You can tune that behavior by setting 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 可用于填充它。

Script-based DataSource initialization is performed, by default, before any JPA EntityManagerFactory beans are created. schema.sql can be used to create the schema for JPA-managed entities and data.sql can be used to populate it. While we do not recommend using multiple data source initialization technologies, if you want script-based DataSource initialization to be able to build upon the schema creation performed by Hibernate, set configprop:spring.jpa.defer-datasource-initialization[] to true. This will defer data source initialization until after any EntityManagerFactory beans have been created and initialized. schema.sql can then be used to make additions to any schema creation performed by Hibernate and data.sql can be used to populate it.

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

The initialization scripts support -- for single line comments and /* */ for block comments. Other comment formats are not supported.

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

If you are using a Higher-level Database Migration Tool, like Flyway or Liquibase, you should use them alone to create and initialize the schema. Using the basic schema.sql and data.sql scripts alongside Flyway or Liquibase is not recommended and support will be removed in a future release.

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

If you need to initialize test data using a higher-level database migration tool, please see the sections about Flyway and Liquibase.

Initialize a Spring Batch Database

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

If you use Spring Batch, it comes pre-packaged with SQL initialization scripts for most popular database platforms. Spring Boot can detect your database type and execute those scripts on startup. If you use an embedded database, this happens by default. You can also enable it for any database type, as shown in the following example:

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

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

You can also switch off the initialization explicitly by setting spring.batch.jdbc.initialize-schema to never.

Use a Higher-level Database Migration Tool

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

Spring Boot supports two higher-level migration tools: Flyway and Liquibase.

Execute Flyway Database Migrations on Startup

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

To automatically run Flyway database migrations on startup, add the org.flywaydb:flyway-core to your classpath.

通常,迁移是 V<VERSION>__<NAME>.sql 形式的脚本(<VERSION> 是用下划线分隔的版本,比如 “1” 或 "`2_1`”)。默认情况下,它们位于名为 classpath:db/migration 的目录中,但是您可以通过设置 spring.flyway.locations 来修改该位置。这是一个用逗号分隔的一个或多个 classpath:filesystem: 位置的列表。例如,以下配置将在默认 classpath 位置和 /opt/migration 目录中搜索脚本:

Typically, migrations are scripts in the form V<VERSION>__<NAME>.sql (with <VERSION> an underscore-separated version, such as '`1’ or '`2_1’). By default, they are in a directory called classpath:db/migration, but you can modify that location by setting spring.flyway.locations. This is a comma-separated list of one or more classpath: or filesystem: locations. For example, the following configuration would search for scripts in both the default classpath location and the /opt/migration directory:

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

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

You can also add a special {vendor} placeholder to use vendor-specific scripts. Assume the following:

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

与使用 db/migration 相反,前面的配置根据数据库的类型设置要使用的目录(比如对 MySQL 的 db/migration/mysql)。受支持数据库的列表在 {code-spring-boot-src}/jdbc/DatabaseDriver.java[DatabaseDriver] 中。

Rather than using db/migration, the preceding configuration sets the directory to use according to the type of the database (such as db/migration/mysql for MySQL). The list of supported databases is available in {code-spring-boot-src}/jdbc/DatabaseDriver.java[DatabaseDriver].

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

Migrations can also be written in Java. Flyway will be auto-configured with any beans that implement JavaMigration.

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

{code-spring-boot-autoconfigure-src}/flyway/FlywayProperties.java[FlywayProperties] provides most of Flyway’s settings and a small set of additional properties that can be used to disable the migrations or switch off the location checking. If you need more control over the configuration, consider registering a FlywayConfigurationCustomizer bean.

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

Spring Boot calls Flyway.migrate() to perform the database migration. If you would like more control, provide a @Bean that implements {code-spring-boot-autoconfigure-src}/flyway/FlywayMigrationStrategy.java[FlywayMigrationStrategy].

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

Flyway supports SQL and Java callbacks. To use SQL-based callbacks, place the callback scripts in the classpath:db/migration directory. To use Java-based callbacks, create one or more beans that implement Callback. Any such beans are automatically registered with Flyway. They can be ordered by using @Order or by implementing Ordered. Beans that implement the deprecated FlywayCallback interface can also be detected, however they cannot be used alongside Callback beans.

默认情况下,Flyway 会自动装配您上下文中的 (@Primary) DataSource 并将其用于迁移。如果您希望使用其他 DataSource,您可以创建一个 DataSource 并且将它的 @Bean 标记为 @FlywayDataSource。如果您这样做了并且想要两个数据源,请记住再创建一个数据源,并将其标记为 @Primary。或者,您可以通过在外部属性中设置 spring.flyway.[url,user,password] 来使用 Flyway 的原生 DataSource。设置 spring.flyway.urlspring.flyway.user 足以使 Flyway 使用它自己的 DataSource。如果这三个属性都未设置,将使用其等效的 spring.datasource 属性的值。

By default, Flyway autowires the (@Primary) DataSource in your context and uses that for migrations. If you like to use a different DataSource, you can create one and mark its @Bean as @FlywayDataSource. If you do so and want two data sources, remember to create another one and mark it as @Primary. Alternatively, you can use Flyway’s native DataSource by setting spring.flyway.[url,user,password] in external properties. Setting either spring.flyway.url or spring.flyway.user is sufficient to cause Flyway to use its own DataSource. If any of the three properties has not been set, the value of its equivalent spring.datasource property will be used.

您还可以使用 Flyway 为特定的场景提供数据。例如,您可以在 src/test/resources 中放置特定于测试的迁移,这些迁移只在您的应用程序启动测试时运行。此外,您还可以使用特定于配置文件的配置来定制 spring.flyway.locations,以便在特定的配置文件处于活动状态时才运行某些迁移。例如,在 application-dev.properties 中,您可以指定以下设置:

You can also use Flyway to provide data for specific scenarios. For example, you can place test-specific migrations in src/test/resources and they are run only when your application starts for testing. Also, you can use profile-specific configuration to customize spring.flyway.locations so that certain migrations run only when a particular profile is active. For example, in application-dev.properties, you might specify the following setting:

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

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

With that setup, migrations in dev/db/migration run only when the dev profile is active.

Execute Liquibase Database Migrations on Startup

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

To automatically run Liquibase database migrations on startup, add the org.liquibase:liquibase-core to your classpath.

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

When you add the org.liquibase:liquibase-core to your classpath, database migrations run by default for both during application startup and before your tests run. This behavior can be customized by using the configprop:spring.liquibase.enabled[] property, setting different values in the main and test configurations. It is not possible to use two different ways to initialize the database (for example Liquibase for application startup, JPA for test runs).

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

By default, the master change log is read from db/changelog/db.changelog-master.yaml, but you can change the location by setting spring.liquibase.change-log. In addition to YAML, Liquibase also supports JSON, XML, and SQL change log formats.

默认情况下,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 属性的值。

By default, Liquibase autowires the (@Primary) DataSource in your context and uses that for migrations. If you need to use a different DataSource, you can create one and mark its @Bean as @LiquibaseDataSource. If you do so and you want two data sources, remember to create another one and mark it as @Primary. Alternatively, you can use Liquibase’s native DataSource by setting spring.liquibase.[driver-class-name,url,user,password] in external properties. Setting either spring.liquibase.url or spring.liquibase.user is sufficient to cause Liquibase to use its own DataSource. If any of the three properties has not been set, the value of its equivalent spring.datasource property will be used.

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

See {code-spring-boot-autoconfigure-src}/liquibase/LiquibaseProperties.java[LiquibaseProperties] for details about available settings such as contexts, the default schema, and others.

Use Flyway for test-only migrations

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

If you want to create Flyway migrations which populate your test database, place them in src/test/resources/db/migration. A file named, for example, src/test/resources/db/migration/V9999__test-data.sql will be executed after your production migrations and only if you’re running the tests. You can use this file to create the needed test data. This file will not be packaged in your uber jar or your container.

Use Liquibase for test-only migrations

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

If you want to create Liquibase migrations which populate your test database, you have to create a test changelog which also includes the production changelog.

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

First, you need to configure Liquibase to use a different changelog when running the tests. One way to do this is to create a Spring Boot test profile and put the Liquibase properties in there. For that, create a file named src/test/resources/application-test.properties and put the following property in there:

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

这将配置 Liquibase 在 test 概要文件 中运行时使用其他变更日志。

This configures Liquibase to use a different changelog when running in the test profile.

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

Now create the changelog file at 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。

This changelog will be used when the tests are run and it will not be packaged in your uber jar or your container. It includes the production changelog and then declares a new changeset, whose runOrder: last setting specifies that it runs after all the production changesets have been run. You can now use for example the insert changeset to insert data or the sql changeset to execute SQL directly.

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

The last thing to do is to configure Spring Boot to activate the test profile when running tests. To do this, you can add the @ActiveProfiles("test") annotation to your @SpringBootTest annotated test classes.

Depend Upon an Initialized Database

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

Database initialization is performed while the application is starting up as part of application context refresh. To allow an initialized database to be accessed during startup, beans that act as database initializers and beans that require that database to have been initialized are detected automatically. Beans whose initialization depends upon the database having been initialized are configured to depend upon those that initialize it. If, during startup, your application tries to access the database and it has not been initialized, you can configure additional detection of beans that initialize the database and require the database to have been initialized.

Detect a Database Initializer

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

Spring Boot will automatically detect beans of the following types that initialize an SQL database:

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

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

If you are using a third-party starter for a database initialization library, it may provide a detector such that beans of other types are also detected automatically. To have other beans be detected, register an implementation of DatabaseInitializerDetector in META-INF/spring.factories.

Detect a Bean That Depends On Database Initialization

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

Spring Boot will automatically detect beans of the following types that depends upon database initialization:

  • AbstractEntityManagerFactoryBean (unless configprop:spring.jpa.defer-datasource-initialization[] is set to true)

  • DSLContext (jOOQ)

  • EntityManagerFactory (unless configprop:spring.jpa.defer-datasource-initialization[] is set to true)

  • JdbcClient

  • JdbcOperations

  • NamedParameterJdbcOperations

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

If you are using a third-party starter data access library, it may provide a detector such that beans of other types are also detected automatically. To have other beans be detected, register an implementation of DependsOnDatabaseInitializationDetector in META-INF/spring.factories. Alternatively, annotate the bean’s class or its @Bean method with @DependsOnDatabaseInitialization.