8.2.3 开启数据库迁移
一种途径是通过 Spring Boot 的 spring.jpa.hibernate.ddl-auto
属性将 hibernate.hbm2ddl.auto
属性设置为 create
、 create-drop
或 update
。例如,要把 hibernate.hbm2ddl.auto
设置为 create-drop
,我们可以在 application.yml 里加入如下内容:
spring:
jpa:
hibernate:
ddl-auto: create-drop
然而,这对生产环境来说并不理想,因为应用程序每次重启数据库,Schema 就会被清空,从头开始重建。它可以设置为 update
,但就算这样,我们也不建议将其用于生产环境。
还有一个途径。我们可以在 schema.sql 里定义 Schema。在第一次运行时,这么做没有问题,但随后每次启动应用程序时,这个初始化脚本都会失败,因为数据表已经存在了。这就要求在书写初始化脚本时格外注意,不要重复执行那些已经做过的工作。
一个比较好的选择是使用数据库迁移库(database migration library)。它使用一系列数据库脚本,而且会记录哪些已经用过了,不会多次运用同一个脚本。应用程序的每个部署包里都包含了这些脚本,数据库可以和应用程序保持一致。
Spring Boot 为两款流行的数据库迁移库提供了自动配置支持。
Flyway( http://flywaydb.org )
Liquibase( http://www.liquibase.org )
当你想要在 Spring Boot 里使用其中某一个库时,只需在项目里加入对应的依赖,然后编写脚本就可以了。让我们先从 Flyway 开始了解吧。
1. 用 Flyway 定义数据库迁移过程
Flyway 是一个非常简单的开源数据库迁移库,使用 SQL 来定义迁移脚本。它的理念是,每个脚本都有一个版本号,Flyway 会顺序执行这些脚本,让数据库达到期望的状态。它也会记录已执行的脚本状态,不会重复执行。
在阅读列表应用程序这里,我们先从一个没有数据表和数据的空数据库开始。因此,这个脚本里需要先创建 Reader
和 Book
表,包含外键约束和初始化数据。代码清单 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 应用程序在云端使用更方便。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论