Spring Boot「06」Loading initial data

440 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第06天,点击查看活动详情

开发过程中,在对某些服务进行单元测试时离不开对数据库的依赖。此时,我们在测试时需要使用内存数据库搭配建表、插表语句来为服务提供基本的测试数据。今天,我们将来看一下 Spring Boot 中如何在启动时创建、并加载初始数据。

01-初始化数据库的方式

  1. 使用 JPA
    • spring.jpa.generate-ddl=true|false 属性来控制是否开启 DDL 生成
  2. 使用 Hibernate
    • spring.jpa.hibernate.ddl-auto=none|validate|update|create|create-drop 属性来控制使用 Hibernate 初始化数据库时的行为。 在使用内存数据库(例如 h2/hsqldb/derby)时,该属性存在一个默认值:未检测到 schema manager 时,默认值为 create-drop;其他情况,默认值为 none。 当上述属性值为 create 或 crate-drop 时,Hibernate 在启动时还会读取 classpath 根目录下的 import.sql 并执行(不过注意:这是 Hibernate 的特性,并非 Spring 的)。
  3. 使用 SQL Scripts
    • Spring Boot 会加载 classpath 根目录下的 schema.sql 和 data.sql;
    • spring.sql.init.platform=platform_value 属性可以使 Spring Boot 加载 schema-platformvalue.sqldata{platform_value}.sql 和 data-{platform_value}.sql,其中 platform_value 可以是 mysql/oracle/h2 等等;
    • 默认情况下,只有在使用内存数据库时,Spring Boot 才会加载上述的 SQL Scripts,这个行为可以通过属性 spring.sql.init.mode=always|never|embedded 来控制:always 指使用任何数据库时都初始化数据库,never 指从不初始化数据库,embedded 指使用内存数据库时初始化数据库;
    • 默认情况下,Spring Boot 执行 SQL Scripts 时是 fail-fast 的,这个行为可以通过属性spring.sql.init.continue-on-error=true|false调整;
    • 默认情况下,使用 SQL Scripts 初始化数据库发生在 JPA EntityManagerFactory Bean 创建之前。 尽管不推荐方式1-3中的多种初始化方式混合使用,但是如果想在 Hibernate 自动生成 DDL 的基础上再执行 schema.sql 和 data.sql,可以将属性spring.jpa.defer-datasource-initialization设置为 true。 意味着将 SQL Scripts 的执行事件延迟(defer)到 EntityManagerFactory Bean 创建之后。schema.sql 可以在 Hibernate 生成的 DDL 基础上做额外的动作,data.sql 可以用来填充数序。

spring.jpa.hibernate.ddl-auto不同取值的含义:

  • create,如果表格已存在,则先删除,然后创建新的表格;

  • update,修改已存在的 schema,并不会删除任何表或列,即使它们不再被应用使用;

  • create-drop,与 create 类似,只不过在所有操作都完成以后,会删掉数据库,一般用于 unit test

  • validate,仅验证表和列是否存在;若不存在,则抛异常;

  • none,指不开启 DDL 自动生成

[1] Database Initialization

02-内存数据库 H2

接下来,我们用上节中介绍的第2、3种方法,搭配 H2 数据库,演示一下 Spring Boot 是如何初始化数据库的。 首先,在 pom.xml 中引入 h2 和 spring-boot-starter-data-jpa 依赖:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.1.214</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>2.7.4</version>
</dependency>

接下来,在 application.properties 中配置内存数据库。

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

jdbc:h2:mem:testdb 指使用内存数据库(易失),数据库名为 testdb。如果需要将数据库持久化在磁盘上,可以将mem:testdb修改为file:/data/file/path。 然后,创建一个 Entity 类 Country,它存储在 COUNTRY 表中。

@Entity
@Table(name = "COUNTRY")
public class Country {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;
    @Column(nullable = false)
    private String name;
    // ... constructor & getters & setters
}

接下来,我们先只使用 Hibernate(即上节中的第 2 种方法)来自动生成数据库表。开启自动生成功能,需要在 application.properties 中增加 spring.jpa.hibernate.ddl-auto=create-drop。 到目前为止,运行程序就会在内存数据库中创建 COUNTRY 表。 Hibernate 会使用 org.hibernate.SQL logger 打印建表语句,如果要从日志中看到,需要在 application.properties 中增加debug=true,如下所示:

org.hibernate.SQL: drop table if exists country CASCADE 
org.hibernate.SQL: create table country (id integer generated by default as identity, name varchar(255) not null, primary key (id))

为了方便查询 h2 数据库的状态,我们打开 H2 内置的 GUI 控制台,只需要在 application.properties 中添加spring.h2.console.enabled=true。 然后就可以通过浏览器访问 http://localhost:8080/h2-console,如下所示:

h2-console.png

输入之前配置的数据库连接信息即可登录。

配置完成后,我们将上节中介绍的 SQL Scripts(即第 3 种方法)在 Hibernate 生成的 schema 基础上再通过 schema.sql 和 data.sql 完成数据库的初始化。 首先,我们需要在 application.properties 中添加spring.jpa.defer-datasource-initialization=true,表示我们将 SQL Scripts 的初始化延迟到 Hibernate 之后。 然后,在 resources 下添加 schema.sql:

CREATE TABLE IF NOT EXISTS country (
     id   INTEGER      NOT NULL AUTO_INCREMENT,
     name VARCHAR(128) NOT NULL,
     PRIMARY KEY (id)
);

再添加 data.sql,向创建的表格中插入 5 条数据:

INSERT INTO COUNTRY (id, name) VALUES (1, 'USA');
INSERT INTO COUNTRY (id, name) VALUES (2, 'France');
INSERT INTO COUNTRY (id, name) VALUES (3, 'Brazil');
INSERT INTO COUNTRY (id, name) VALUES (4, 'Italy');
INSERT INTO COUNTRY (id, name) VALUES (5, 'Canada');

好了,到此为止,所有的准备工作就都完成啦。让我们来运行下程序,看看效果吧。 登录到 h2-console 之后,使用select * from country;查询 data.sql 中的数据有没有插入到数据库中,结果如下所示:

h2-console-results.png

[1] Spring Boot With H2 Database

03-总结

今天,我们学习了 Spring Boot 中加载初始化数据的三种方式,并且以 Hibernate + SQL Scripts 配合的方式,搭配 H2 内存数据库,演示了整个过程。这种加载初始化数据的方式在做的单元测试是非常有用,可以提高测试效率。