数据库初始化
根据你的技术栈的不同,SQL 数据库的初始化方式也有所不同。当然,你也可以手动初始化,前提是数据库是一个单独的进程。建议使用单一机制生成模式。
使用 Hibernate 初始化数据库
你可以设置 `spring.jpa.hibernate.ddl-auto` 来控制 Hibernate 的数据库初始化。支持的值包括 `none`、`validate`、`update`、`create` 和 `create-drop`。Spring Boot 会根据你是否使用嵌入式数据库来为你选择默认值。通过查看 `Connection` 类型和 JDBC url 来识别嵌入式数据库。`hsqldb`、`h2` 或 `derby` 是嵌入式数据库,其他则不是。如果识别出嵌入式数据库并且没有检测到模式管理器(Flyway 或 Liquibase),则 `ddl-auto` 默认值为 `create-drop`。在所有其他情况下,它默认为 `none`。
从内存数据库切换到“真实”数据库时要小心,不要假设新平台中存在表和数据。你必须显式设置 `ddl-auto` 或使用其他机制来初始化数据库。
可以通过启用 `org.hibernate.SQL` 日志记录器来输出模式创建信息。如果你启用了调试模式,则会自动为你完成此操作。 |
此外,如果Hibernate从头开始创建模式(即,如果`ddl-auto`属性设置为`create`或`create-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-locations`和`spring.sql.init.data-locations`分别自定义这些模式和数据脚本的位置。`optional:`前缀表示即使文件不存在,应用程序也会启动。如果希望文件不存在时应用程序启动失败,请移除`optional:`前缀。
此外,Spring Boot还会处理`optional:classpath*:schema-${platform}.sql`和`optional:classpath*:data-${platform}.sql`文件(如果存在),其中`${platform}`是`spring.sql.init.platform`的值。这允许您在必要时切换到特定于数据库的脚本。例如,您可以选择将其设置为数据库的厂商名称(`hsqldb`、`h2`、`oracle`、`mysql`、`postgresql`等)。
默认情况下,只有在使用嵌入式内存数据库时才会执行SQL数据库初始化。要始终初始化SQL数据库,无论其类型如何,请将`spring.sql.init.mode`设置为`always`。同样,要禁用初始化,请将`spring.sql.init.mode`设置为`never`。默认情况下,Spring Boot启用其基于脚本的数据库初始化程序的快速失败功能。这意味着如果脚本导致异常,应用程序将无法启动。您可以通过设置`spring.sql.init.continue-on-error`来调整此行为。
默认情况下,基于脚本的`DataSource`初始化在创建任何JPA `EntityManagerFactory` bean之前执行。`schema.sql`可用于创建JPA管理实体的模式,`data.sql`可用于填充它。虽然我们不建议使用多种数据源初始化技术,但如果您希望基于脚本的`DataSource`初始化能够建立在Hibernate执行的模式创建之上,请将`spring.jpa.defer-datasource-initialization`设置为`true`。这将推迟数据源初始化,直到任何`EntityManagerFactory` bean创建并初始化之后。然后可以使用`schema.sql`对Hibernate执行的任何模式创建进行添加,并可以使用`data.sql`填充它。
初始化脚本支持`--`用于单行注释和`/* */`用于块注释。不支持其他注释格式。 |
如果您正在使用更高级别的数据库迁移工具,如Flyway或Liquibase,则应单独使用它们来创建和初始化模式。不建议同时使用基本的`schema.sql`和`data.sql`脚本以及Flyway或Liquibase,并且将在未来的版本中删除对此的支持。
初始化Spring Batch数据库
如果您使用Spring Batch,它预先打包了针对大多数流行数据库平台的SQL初始化脚本。Spring Boot可以检测您的数据库类型并在启动时执行这些脚本。如果您使用嵌入式数据库,则默认情况下会发生这种情况。您也可以为任何数据库类型启用它,如下例所示
-
属性
-
YAML
spring.batch.jdbc.initialize-schema=always
spring:
batch:
jdbc:
initialize-schema: "always"
您还可以通过将`spring.batch.jdbc.initialize-schema`设置为`never`来显式关闭初始化。
使用更高级别的数据库迁移工具
在启动时执行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`目录中的脚本
-
属性
-
YAML
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
spring:
flyway:
locations: "classpath:db/migration,filesystem:/opt/migration"
您还可以添加一个特殊的`{vendor}`占位符来使用特定于厂商的脚本。假设如下
-
属性
-
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`来对它们进行排序。还可以检测到实现已弃用的`FlywayCallback`接口的bean,但是它们不能与`Callback` bean一起使用。
默认情况下,Flyway在您的上下文环境中自动装配(`@Primary`)`DataSource`并将其用于迁移。如果您想使用不同的`DataSource`,您可以创建一个并将其`@Bean`标记为`@FlywayDataSource`。如果您这样做并且想要两个数据源,请记住创建另一个并将其标记为`@Primary`。或者,您可以通过在外部属性中设置`spring.flyway.[url,user,password]`来使用Flyway的原生`DataSource`。设置`spring.flyway.url`或`spring.flyway.user`就足以使Flyway使用其自己的`DataSource`。如果没有设置这三个属性中的任何一个,则将使用其等效的`spring.datasource`属性的值。
您还可以使用Flyway为特定场景提供数据。例如,您可以将特定于测试的迁移放在`src/test/resources`中,只有当您的应用程序启动进行测试时,它们才会运行。此外,您可以使用特定于配置文件的配置来自定义`spring.flyway.locations`,以便只有在激活特定配置文件时才运行某些迁移。例如,在`application-dev.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`属性自定义此行为,在`main`和`test`配置中设置不同的值。不可能使用两种不同的方法来初始化数据库(例如,应用程序启动使用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.url`或`spring.liquibase.user`就足以使Liquibase使用其自己的`DataSource`。如果没有设置这三个属性中的任何一个,则将使用其等效的`spring.datasource`属性的值。
有关可用设置(例如上下文、默认模式等)的详细信息,请参阅LiquibaseProperties
。
将Flyway用于仅测试迁移
如果要创建填充测试数据库的Flyway迁移,请将它们放在src/test/resources/db/migration
目录下。例如,名为src/test/resources/db/migration/V9999__test-data.sql
的文件将在生产迁移之后执行,并且只有在运行测试时才会执行。您可以使用此文件创建所需的测试数据。此文件不会打包到您的uber jar或容器中。
使用Liquibase进行仅测试迁移
如果要创建填充测试数据库的Liquibase迁移,则必须创建一个测试变更日志,其中也包含生产变更日志。
首先,需要配置Liquibase在运行测试时使用不同的变更日志。一种方法是创建一个Spring Boot test
profile并将Liquibase属性放在其中。为此,创建一个名为src/test/resources/application-test.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
profile中运行时使用不同的变更日志。
现在在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或容器中。它包含生产变更日志,然后声明一个新的changeset,其runOrder: last
设置指定它在所有生产changeset运行之后运行。现在,您可以例如使用insert changeset插入数据或使用sql changeset直接执行SQL。
最后一步是配置Spring Boot在运行测试时激活test
profile。为此,您可以将@ActiveProfiles("test")
注解添加到您的@SpringBootTest
注解的测试类中。
依赖于已初始化的数据库
数据库初始化在应用程序启动期间作为应用程序上下文刷新的一部分执行。为了允许在启动期间访问已初始化的数据库,会自动检测充当数据库初始化程序的bean和需要已初始化数据库的bean。其初始化依赖于数据库已初始化的bean被配置为依赖于初始化它的bean。如果在启动期间,您的应用程序尝试访问数据库但尚未初始化,您可以配置对初始化数据库的bean和需要已初始化数据库的bean的额外检测。
检测数据库初始化程序
Spring Boot将自动检测以下类型的初始化SQL数据库的bean:
-
DataSourceScriptDatabaseInitializer
-
EntityManagerFactory
-
Flyway
-
FlywayMigrationInitializer
-
R2dbcScriptDatabaseInitializer
-
SpringLiquibase
如果您正在使用数据库初始化库的第三方启动器,它可能会提供一个检测器,以便其他类型的bean也能自动检测到。要检测其他bean,请在META-INF/spring.factories
中注册DatabaseInitializerDetector
的实现。
检测依赖于数据库初始化的Bean
Spring Boot将自动检测以下类型的依赖于数据库初始化的bean:
-
AbstractEntityManagerFactoryBean
(除非spring.jpa.defer-datasource-initialization
设置为true
) -
DSLContext
(jOOQ) -
EntityManagerFactory
(除非spring.jpa.defer-datasource-initialization
设置为true
) -
JdbcClient
-
JdbcOperations
-
NamedParameterJdbcOperations
如果您正在使用第三方启动器数据访问库,它可能会提供一个检测器,以便其他类型的bean也能自动检测到。要检测其他bean,请在META-INF/spring.factories
中注册DependsOnDatabaseInitializationDetector
的实现。或者,使用@DependsOnDatabaseInitialization
注解bean的类或其@Bean
方法。