返回介绍

8.2.3 开启数据库迁移

发布于 2025-04-21 21:10:11 字数 7736 浏览 0 评论 0 收藏

一种途径是通过 Spring Boot 的 spring.jpa.hibernate.ddl-auto 属性将 hibernate.hbm2ddl.auto 属性设置为 createcreate-dropupdate 。例如,要把 hibernate.hbm2ddl.auto 设置为 create-drop ,我们可以在 application.yml 里加入如下内容:

spring:
  jpa:
    hibernate:
      ddl-auto: create-drop

然而,这对生产环境来说并不理想,因为应用程序每次重启数据库,Schema 就会被清空,从头开始重建。它可以设置为 update ,但就算这样,我们也不建议将其用于生产环境。

还有一个途径。我们可以在 schema.sql 里定义 Schema。在第一次运行时,这么做没有问题,但随后每次启动应用程序时,这个初始化脚本都会失败,因为数据表已经存在了。这就要求在书写初始化脚本时格外注意,不要重复执行那些已经做过的工作。

一个比较好的选择是使用数据库迁移库(database migration library)。它使用一系列数据库脚本,而且会记录哪些已经用过了,不会多次运用同一个脚本。应用程序的每个部署包里都包含了这些脚本,数据库可以和应用程序保持一致。

Spring Boot 为两款流行的数据库迁移库提供了自动配置支持。

当你想要在 Spring Boot 里使用其中某一个库时,只需在项目里加入对应的依赖,然后编写脚本就可以了。让我们先从 Flyway 开始了解吧。

1. 用 Flyway 定义数据库迁移过程

Flyway 是一个非常简单的开源数据库迁移库,使用 SQL 来定义迁移脚本。它的理念是,每个脚本都有一个版本号,Flyway 会顺序执行这些脚本,让数据库达到期望的状态。它也会记录已执行的脚本状态,不会重复执行。

在阅读列表应用程序这里,我们先从一个没有数据表和数据的空数据库开始。因此,这个脚本里需要先创建 ReaderBook 表,包含外键约束和初始化数据。代码清单 8-2 就是从空数据库到可用状态的 Flyway 脚本。

代码清单 8-2 Flyway 数据库初始脚本

create table Reader (        ←---创建 Reader 表
  id serial primary key,
  username varchar(25) unique not null,
  password varchar(25) not null,
  fullname varchar(50) not null
);

create table Book (         ←---创建 Book 表
  id serial primary key,
  author varchar(50) not null,
  description varchar(1000) not null,
  isbn varchar(10) not null,
  title varchar(250) not null,
  reader_username varchar(25) not null,
  foreign key (reader_username) references Reader(username)
);

create sequence hibernate_sequence;      ←---定义序列

insert into Reader (username, password, fullname)    ←---Reader 的初始数据
            values ('craig', 'password', 'Craig Walls');

如你所见,Flyway 脚本就是 SQL。让其发挥作用的是其在 Classpath 里的位置和文件名。Flyway 脚本都遵循一个命名规范,含有版本号,具体如图 8-1 所示。

图 8-1 用版本号命名的 Flyway 脚本

所有 Flyway 脚本的名字都以大写字母 V 开头,随后是脚本的版本号。后面跟着两个下划线和对脚本的描述。因为这是整个迁移过程中的第一个脚本,所以它的版本是 1。描述可以很灵活,主要用来帮助理解脚本的用途。稍后我们需要向数据库添加新表,或者向已有数据表添加新字段。可以再创建一个脚本,标明版本号为 2。

Flyway 脚本需要放在相对于应用程序 Classpath 根路径的/db/migration 路径下。因此,项目中,脚本需要放在 src/main/resources/db/migration 里。

你还需要将 spring.jpa.hibernate.ddl-auto 设置为 none ,由此告知 Hibernate 不要创建数据表。这关系到 application.yml 中的如下内容:

spring:
  jpa:
    hibernate:
      ddl-auto: none

剩下的就是将 Flyway 添加为项目依赖。在 Gradle 里,此依赖是这样的:

compile("org.flywaydb:flyway-core")

在 Maven 项目里, <dependency> 是这样的:

<dependency>
  <groupId>org.flywayfb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>

在应用程序部署并运行起来后,Spring Boot 会检测到 Classpath 里的 Flyway,自动配置所需的 Bean。Flyway 会依次查看/db/migration 里的脚本,如果没有执行过就运行这些脚本。每个脚本都执行过后,向 schema_version 表里写一条记录。应用程序下次启动时,Flyway 会先看 schema_version 里的记录,跳过那些脚本。

2. 用 Liquibase 定义数据库迁移过程

Flyway 用起来很简便,在 Spring Boot 自动配置的帮助下尤其如此。但是,使用 SQL 来定义迁移脚本是一把双刃剑。SQL 用起来便捷顺手,却要冒着只能在一个数据库平台上使用的风险。

Liquibase 并不局限于特定平台的 SQL,可以用多种格式书写迁移脚本,不用关心底层平台(其中包括 XML、YAML 和 JSON)。如果你有这个期望的话,Liquibase 当然也支持 SQL 脚本。

要在 Spring Boot 里使用 Liquibase,第一步是添加依赖。Gradle 里的依赖是这样的:

compile("org.liquibase:liquibase-core")

对于 Maven 项目,你需要添加如下 <dependency>

<dependency>
  <groupId>org.liquibase</groupId>
  <artifactId>liquibase-core</artifactId>
</dependency>

有了这个依赖,Spring Boot 自动配置就能接手,配置好用于支持 Liquibase 的 Bean。默认情况下,那些 Bean 会在/db/changelog(相对于 Classpath 根目录)里查找 db.changelog-master.yaml 文件。这个文件里都是迁移脚本。代码清单 8-3 的初始化脚本为阅读列表应用程序进行了数据库初始化。

代码清单 8-3 用于阅读列表数据库的 Liquibase 初始化脚本

databaseChangeLog:
  - changeSet:
      id: 1         ←---变更集 ID
      author: habuma
      changes:
        - createTable:
            tableName: reader      ←---创建 reader 表
            columns:
              - column:
                  name: username
                  type: varchar(25)
                  constraints:
                    unique: true
                    nullable: false
              - column:
                  name: password
                  type: varchar(25)
                  constraints:
                    nullable: false
              - column:
                  name: fullname
                  type: varchar(50)
                  constraints:
                    nullable: false
        - createTable:
            tableName: book       ←---创建 book 表
            columns:
              - column:
                  name: id
                  type: bigserial
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: author
                  type: varchar(50)
                  constraints:
                    nullable: false
              - column:
                  name: description
                  type: varchar(1000)
                  constraints:
                    nullable: false
              - column:
                  name: isbn
                  type: varchar(10)
                  constraints:
                    nullable: false
              - column:
                  name: title
                  type: varchar(250)
                  constraints:
                    nullable: false
              - column:
                  name: reader_username
                  type: varchar(25)
                  constraints:
                    nullable: false
                    references: reader(username)
                    foreignKeyName: fk_reader_username
        - createSequence:              ←---定义序列
            sequenceName: hibernate_sequence
        - insert:
            tableName: reader       ←---插入 reader 的初始记录
            columns:
              - column:
                 name: username
                 value: craig
              - column:
                 name: password
                 value: password
              - column:
                 name: fullname
                 value: Craig Walls

如你所见,比起等效的 Flyway SQL 脚本,YAML 格式略显繁琐,但看起来还是很清晰的,而且这个脚本不与任何特定的数据库平台绑定。

与 Flyway 不同,Flyway 有多个脚本,每个脚本对应一个变更集。Liquibase 变更集都集中在一个文件里。请注意, changeset 命令后的那行有一个 id 属性,要对数据库进行后续变更。可以添加一个新的 changeset ,只要 id 不一样就行。此外, id 属性也不一定是数字,可以包含任意内容。

应用程序启动时,Liquibase 会读取 db.changelog-master.yaml 里的变更集指令集,与之前写入 databaseChangeLog 表里的内容做对比,随后执行未运行过的变更集。

虽然这里的例子使用的是 YAML 格式,但你也可以任意选择 Liquibase 所支持的其他格式,比如 XML 或 JSON。只需简单地设置 liquibase.change-log 属性(在 application.properties 或 application.yml 里),标明希望 Liquibase 加载的文件即可。举个例子,要使用 XML 变更集,可以这样设置 liquibase.change-log

liquibase:
  change-log: classpath:/db/changelog/db.changelog-master.xml

Spring Boot 的自动配置让 Liquibase 和 Flyway 的使用变得轻而易举。但实际上所有数据库迁移库都有更多功能,这里不便一一列举。建议大家参考官方文档,了解更多详细内容。

我们已经了解了如何将 Spring Boot 应用程序部署到传统的 Java 应用服务器上,基本就是创建一个 SpringBootServletInitializer 的子类,调整构建说明来生成一个 WAR 文件,而非 JAR 文件。接下来我们会看到,Spring Boot 应用程序在云端使用更方便。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。