一文详解Liquibase如何自动化数据库脚本部署

如今,大多数组织都已经在其应用程序中实施了 DevOps 的 CI/CD 流程。不过,其数据库的自动化改造似乎尚未跟上时代。为此,我将向您介绍一种能够实现自动化脚本部署的数据库产品 --Liquibase。
1
Liquibase 的基本特点
自动化数据库的部署脚本。
以相同的方式部署到不同的环境中。
能够为每次数据库的更改准备好回滚。
能够将部署的所有详细信息集中到一处。
最少化的部署错误。
方便开发人员针对相同的数据库,进行高效的协同编程。
审核逐个更改 (请参考下图)。
下面,我将向您展示如何使用 Liquibase 和 Git 在 Pretius 上,自动化数据库的更改过程。
2
什么是 Liquibase?
Liquibase(简称 LB) 是一个用 Java 编写的开源工具。它以用户熟悉的格式定义了数据库接口,并能够自动生成特定于数据库的 SQL。例如,它将数据库的更改 (每一次更改可称为一个更改集) 放入被称为 changelog 的文件中进行管理。通常,Liquibase 在数据库架构中会自动创建两张表:
DATABASECHANGELOG — 存储有关数据库所有更改信息的表。
DATABASECHANGELOGLOCK — 用于防止用户同时对数据库进行更改。
我将在下面示例中,基于 SQL 编写变更集,以实现对 Oracle 数据库的自动化更改过程。
3
从安装 Liquibase 开始
请通过链接 https://www.liquibase.org/download,选择“仅文件 (Just the files)”的方式,下载 Liquibase 的最新版本。在本文中,我将使用版本:4.3.0 build 09.02.2021。
在将其 zip 文件夹解压缩后,您必须将新的路径变量 (New Path System Variable) 设置为计算机上的 liquibase-version#bin 文件夹。同时,为了使 Liquibase 正常工作,您还必须安装 Java。
通过在 CLI 工具 (在此,我使用的是 Visual Studio Code) 输入:Liquibase—version,您将能看到:
如果您在文件中使用的是 UTF8 编码,那么请务必在 liquibase.bat 文件中添加一行:IF NOT DEFINED JAVA_OPTS set JAVA_OPTS=-Dfile.encoding=UTF–8。
4
配置项目和 Liquibase
下面,让我们来组织各个文件 (在本例中,我的 GIT 存储库放在文件夹 HR 中)。在各个文件夹中,我们可以在项目开发的过程中创建不同的文件。如果您有其他类型的对象 (如“创建或替换”类型),那么只需要用它们创建“同义 (synonyms)”文件夹即可。
Liquibase 中的文件组织
#path to our master changelog file
changeLogFile:Liquibase/update.xml
#dbhost and credentials
url: jdbc:oracle:thin:@127.0.0.1:1521/XEPDB1
username: HR
password: XXXXXX
#OJDBC driver localization
classpath:Liquibase/ojdbc8.jar
#schema, whereLiquibasewill store it’s DATABASECHANGELOG and DATABASECHANGELOGLOCK table(if other than HR, remember to add grants to HR!)
liquibaseSchemaName: HR
#default SQL file name generated byLiquibase
outputFile=output_local.sql
#debug mode
loglevel=SEVERE
#extra option fromLiquibase, we don’t need it for now.
liquibase.hub.mode=off
更新了的 Liquibase 文件夹结构
现在,我们创建一个 update.xml 文件,并将它放入带有 OJDBC 文件的、新的 hr/Liquibase 文件夹中:
<?xml version="1.0″ encoding="UTF-8″?><databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"></databaseChangeLog>
5
使用 Oracle Wallet(可选)
如果您的 Oracle 数据库托管在 Oracle 自治数据库上,那么就需要使用 Wallet,通过 Liquibase 去连接它。为此,请下载 Wallet 并记住其密码。
请将您的 WALLET_NAME.ZIP 解压到之前创建的 HR/Liquibase 文件夹中,并编辑 HR/liquibase/wallet_name/ojdbc.properties 文件:
更改 ojdsb.properties
修改后的文件如上图所示。在 javax.net.ssl.trustStorePassword 和 javax.net.ssl.keyStorePassword 行,你可以设置 ATP Wallet 的密码。
在 liquibase_local.properties 文件中,请编辑 URL 一行,并设置连接的名称 (即,来自 Wallet/tnsnames.ora,以及去往 Wallet 的路径):
url: jdbc:oracle:thin:@rgatp28_high?TNS_ADMIN=liquibase/Wallet_RGATP28
当然,请检查您的 sqlnet.ora 文件,确保其“SSL_SERVER_DN_MATCH=yes”,且无需改变其他地方。
6
将 Liquibase 与数据库连接
如果一切设置正确,我们便可以顺利连接上 DEV 数据库。让我们从 HR 文件夹 (Liquibase 的属性文件位置) 处启动 CLI,并输入:
Liquibase—defaultsFile=liquibase_dev.properties updateSQL
VSCode 终端中的 updateSQL 命令
其中:
Liquibase 会调用 LB 的环境路径。
defaultsFile 指定属性文件的名称和位置。如果您将属性文件命名为“liquibase.properties”,那么可以省略此命令。
updateSQL 负责生成 SQL 脚本 (它并不会对数据库执行任何操作)。
几秒钟后,LB 将会生成 output_file.sql:
生成的 output_file.sql
如前所述,如果您在数据库中运行该脚本,它将创建两个表:DATABASECHANGELOG 和 DATABASECHANGELOGLOCK。下面,让我们通过 Liquibase—defaultsFile=liquibase_dev.properties update,来创建这些表。其中的 update 命令是对数据库执行 SQL 语句。完成后,您将看到如下结构:
我们需要创建一个 changelog 文件,并指向包含对象的文件夹。在此,我创建了如下 HR/master.xml 文件:
<?xml version="1.0″ encoding="UTF-8″?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<includeAll path="triggers" relativeToChangelogFile="true"/>
<includeAll path="views" relativeToChangelogFile="true"/>
<includeAll path="types" relativeToChangelogFile="true"/>
<includeAll path="package_spec" relativeToChangelogFile="true"/>
<includeAll path="package_bodies" relativeToChangelogFile="true"/>
</databaseChangeLog>
它指向对象文件夹、及其所有内容。为了将主 changelog 文件 HR/liquibaseupdate.xml 设置为指向 master.xml 文件的路径,您只需添一行:
<include file="./master.xml"/>
在 update.xml 中的 include file="./master.xml"
由于 Liquibase 始终会从 Liquibase_dev.properties 文件和 update.xml 文件处运行,因此我们需要让它能够“看到”所有的文件。
7
跟踪 DML 和 DDL 数据库的更改
我们需要为 DML 和 DDL 类型的更改创建一个单独的 changelog 文件,并将更改集写入其中。为此,我们只需创建一个 changelog.sql 文件,并输入如下内容,以将其标记为 LiquibaseSQL 文件:
—Liquibaseformatted sql
将 changelog.sql 标记为 LiquibaseSQL 文件
我们通过在 master.xml 文件中添加如下内容,以指向新的 changelog:
指向新的 changelog
指向 changelog 或文件夹的顺序是非常重要的。它需要告知 Liquibase 在运行 SQL 时的顺序。我们最好先运行 changelog(其中包含了“create table(...)”),然后再运行使用该表的编译包。
下面,让我们在变更集中创建第一个项目表:
—changeset AUTHOR:CHANGESET_NAME
—comment OPTIONAL COMMENT
YOUR DDL
创建第一个项目表
为了预览到数据库有哪些更改,我们让 LB 生成对应的 SQL 文件。
Liquibase—defaultsFile=liquibase_dev.properties updateSQL
由 Liquibase 生成 SQL 文件
您可能注意到了,LB 通过设置 LOCKED = 1,来锁定 DATABASECHANGELOGLOCK 表。也就是说,当您将脚本运行到 DB 时,列 LOCKED 被设置为 1。而当另一个用户同时运行 LB 时,Liquibase 将为此等待,直到锁定被放开,再创建一个 SHOES 表,将日志的更改插入到 DATABASECHANGELOG 中,并从 DATABASECHANGELOGLOCK 表中释放掉已有的锁。
如果一切正常,如下脚本会被执行到数据库中:
Liquibase—defaultsFile=liquibase_dev.properties update
接着,表 SHOES 会被创建出来。
我们也可以查询到谁、为何、以及何时创建了这张表。
8
跟踪包、视图等其他更改
我们也可以如法创建其他脚本。在此,我通过 2 个单独的文件,创建了一个 SHOES_PKG 包。每个文件都是带有附加参数的唯一变更集,并被标记为 Liquibase 格式的 SQL 文件。
SHOES_BODY 和 SHOES_SPEC SQL 文件
其中:
runOnChange:true ——意味着每次更改包时,Liquibase 都会针对数据库运行该变更集,也就是编译这个包。
stripComments:false ——意味着不要去除代码注释。
因此,LB 在对数据库进行 updateSQL 操作时,就会去编译包的规范 (package spec)、以及包的主体 (package body)。一旦我们在数据库中通过 update 命令编译这些包,它们都会被记录下来。
通过查看 MD5SUM 的列值可知,它是变更集的最后一次校验。也就是说,运行了 updateSQL 后,所有前期被“挂起”的更改都被执行,而且除了锁定 LB 表外,LB 不会在 SQL 中生成任何内容。
运用 updateSQL 检查 output_local.sql
现在,让我们改变 SHOES_PKG 本身,并保存该文件。
更新 SHOES_PKG 本身
那么该文件的校验和会发生变化,LB 将再次编译这个包,并运行更新。
Liquidbase 中的更新

数据库中的更新
Liquibase 将再次编译这个包,并使用 DATABASECHANGELOG 表中的实际 DATEEXECUTED 和新的 MD5SUM 等变更集,去更新相应的行。
9
如何在现有软件项目中安装 Liquibase?
虽然我们好几种方法可以让 Liquibase 为现有的数据库实现自动化,但是我在此只向您展示最实用的两种。您可以从中选择最适合实际需求的一种。
10
当现有的项目中有很多对象时
我们通过在项目的存储库中配置 Liquibase,并保留所有文件的基础上,在 master.xml 文件中添加指向它们的路径。具体而言,在实施 Liquibase 之前,我创建了 2 个过程和 2 个触发器:
P_ADD_JOB_HISTORY
P_SECURE_DML
TRG_SECURE_EMPLOYEES
TRG_UPDATE_JOB_HISTORY

现有的 P_ADD_JOB_HISTORY.sql 文件
您并不需要将“changeset”或“–Liquibaseformatted sql”添加到文件中。
更新后 master.xml 中的文件路径
我在自己的 master.xml 中添加了一个指向 PROCEDURES 文件夹的路径。
下面,让我们运行 LiquibaseupdateSQL,并查看 Liquibase 会执行什么样的 SQL:
Liquibase—defaultsFile=liquibase_dev.properties updateSQL

首次尝试更新 SQL
既然我们的数据库中已经有了这些过程和触发器,我们就需要通过 ChangelogSync 和 ChangelogSyncSQL 命令,避免重复创建。让我们运行 ChangelogSyncSQL,并查看其结果。
Liquibase—defaultsFile=liquibase_dev.properties ChangelogSyncSQL
输出的 SQL 文件为:
可见,SQL 文件只插入了一个 DATABASECHANGELOG 表。它会告知 Liquibase 这些对象已经创建好了,不需要再次运行。现在,我们便可以将其插入到 Oracle 数据库中了:
Liquibase—defaultsFile=liquibase_dev.properties ChangelogSync
此时,在 DATABASECHANGELOG 表中会有 4 个新的变更集:
您也许会问,这些奇怪的“raw”ID 是什么? 为什么作者又被称为“includeAll”呢? 这是因为我们采取了最简单、最快捷的方式,将现有的项目迁移到了 Liquibase 处,而这些变更集是被自动创建的。
当然,您也可以进行一些更改。例如,在 P_ADD_JOB_HISTORY 中,只需添加一个 changeset,就像您在创建新数据库对象时常做的那样。
更改 P_ADD_JOB_HISTORY
然后运行 Liquibase 的更新命令:
现在,Changeset 就带有了合适的作者、ID 等信息。
在上面的示例中,我向您展示了添加现有对象 (可创建或替换) 的简单方法,且无需手动创建变更集。我认为这是将 Liquibase 安装到拥有数百个对象的、现有数据库中的最佳方式。不过,当涉及到库里有不能被替换的对象 (如表格) 时,我们需要使用另一种方式。
11
创建或替换的对象
正如前面所描述过的,请添加对象,并在 master.xml 文件中记下文件夹的路径。接着请运行 ChangelogSync,并让 Liquibase 自动创建 raw/includeAll/filename 的变更集。
由 Liquibase 生成的变更集
当然,您也可以采用更好的方法,为每个文件创建一个变更集,如下图所示:
虽然这会需要更多的工作,但是您可以在日志中获得更全面的信息:
针对无法创建或替换的对象,您同样有两种方法:
对这些对象不做任何操作,但请记住始终为其中的每个更改 (包括:更改表、删除列等) 创建变更集,并将其添加到 changelog.sql 文件中。
创建变更集并将它们标记为过去已被执行过。
在此,我们着重讨论第二种方式。由于在实施 Liquibase 之前我已创建好了 EMPLOYEES 和 JOBS 两张表,因此我会在新的文件夹 HR/scripts_before_Liquibase 中,创建 changelog_ddl.sql 和 changelog_constraints.sql 两个 changelog 文件。此外,我也创建了另一个 scripts_before_liquibase.xml 文件,并将其指向这两个 changelog。其中的“include file”通过优先级的方式,告知 Liquibase 运行脚本的顺序,即:首先创建表,然后创建约束和索引。
新的 scripts_before_liquibase.xml 文件
这两个文件能够方便您在表中创建 ref_constraint 时,避免产生冲突。如下图所示,请记住在 master.xml 文件中,将路径添加到新创建的 XML 文件 (即 HR/script_before_liquibase/scripts_before_liquibase.xml) 中。
下面是为各种表和约束创建的变更集。
在添加了所有的变更集之后,我们将它们标记为已执行的状态。
让我们运行 ChangelogSyncSQL 来进行预览,并让 ChangelogSync 对数据库执行 SQL。
运行 ChangeSyncSQL 和 ChangelogSync
下图展示了 ChangelogSync 命令执行后,更新了的数据库。至此,我们已大功告成,您也可以选用自己喜欢的方式,通过 Liquibase 来实现数据库的自动化。
12
小结
如您所见,通过使用 Liquibase,我们可以在数据库更改的发布过程中,跟踪所有的相关内容。其中,需要开发人员遵守如下流程:
基于变更集的唯一性,并结合 AUTHOR:ID(task) 与文件名 (带 changelog 的文件),将变更集添加到 changelog 中。而且,请不要在没有 Liquibase 的情况下,更改任何内容。
验证待执行的 SQL。
运行数据库的 update 命令 (记住,应当先运行 updateSQL,再执行 update 命令)。
通过检查数据库对象和 DATABASECHANGELOG 表,验证变更集是否已被执行。
关注公众号:拾黑(shiheibook)了解更多 [广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

随时掌握互联网精彩
- 1 为什么中国意味着确定性未来性 7904539
- 2 多地宣布发钱奖励结婚 7808825
- 3 特朗普甩锅中国用的是P过的图 7713564
- 4 加关税降逆差为啥走不通 7618077
- 5 48岁女子怀孕7个月 28岁女儿发声 7523590
- 6 林志玲真的不会老吗 7425985
- 7 印媒称印巴在克什米尔再次交火 7328431
- 8 在香港排队时吸烟将罚3000港元 7233665
- 9 谢霆锋转发王菲看他演唱会的博文 7137107
- 10 马斯克100天内砍掉自己1500亿身家 7044786