LiquiBase中文学习指南

领先的开源数据库更改和部署解决方案。Liquibase 提供独立于数据库的方式,提供快速、安全、可重复的数据库部署

概述

此快速入门为 Liquibase 提供了简要指导,并涉及三个关键主题:

快速入门并不涵盖 Liquibase中提供的每个功能,而是侧重于确保您了解核心概念并能够解决基本用例。

数据库更改管理:状态和迁移方法

有两种方法来管理数据库更改。第一种方法是基于状态的(或声明性的)的 , 其中定义了数据库的所需状态。可以通过对应工具将目标环境与定义的所需状态进行比对(或对比),用于生成允许目标环境与声明状态匹配的迁移脚本。另一种方法是基于迁移(或命令的),其中描述了用于更改数据库状态的特定迁移。通过对应工具能够显式跟踪和排序各个迁移,并将尚未部署到目标环境的迁移正确迁移到目标数据库。

虽然Liquibase能够进行比对(或对比),但它从根本上说是基于迁移的解决方案。Liquibase的比对能力仅用于协助加入新项目,并检查数据库迁移是否得到正确应用。作为基于迁移的解决方案,Liquibase 可以轻松:

  • 跟踪所有建议的数据库更改,包括需要部署的特定顺序、建议/创作更改的人员,并记录更改的目的(作为注释)

  • 清楚地回答数据库更改是否已部署到数据库。实际上,Liquibase 能够管理每个数据库的"版本"。

  • 确切地将更改部署到数据库,包括将数据库提升为特定的"版本"

  • 阻止用户修改已部署到数据库的更改,要么有意重新处理部署的更改,要么前滚。

Liquibase是如何工作的

Liquibase的核心是依靠一种简单的机制来跟踪、版本和部署更改:

  • Liquibase 使用更改日志(是更改的分类)按特定顺序显式列出数据库更改。更改日志中的每个更改都是一个change set。更改日志可以任意嵌套,以帮助组织和管理数据库迁移。
    • 注意: 最佳做法是确保每个change set都尽可能原子性更改,以避免失败的结果使数据库中剩下的未处理的语句处于unknown 状态;不过,可以将大型 SQL 脚本视为单个更改集。
  • Liquibase 使用跟踪表(具体称为DATABASECHANGELOG),该表位于每个数据库上,并跟踪已部署更改日志中的change set
    • 注意:如果 Liquibase所在的数据库没有跟踪表,Liquibase 将创建一个跟踪表。
    • 注意:为了协助处理您未从空白数据库开始的项目,Liquibase具有生成一条更改日志以表示数据库模式当前状态的功能。

使用分类和跟踪表,Liquibase 能够:

  • 跟踪和以版本控制数据库更改 – 用户确切知道已部署到数据库的更改以及尚未部署的更改。
  • 部署更改 — 具体来说,通过将分类(ledger)中的内容与跟踪表中的内容进行比较,Liquibase 只能将以前尚未部署到数据库的更改部署到数据库中。
    • 注意:Liquibase 具有上下文、标签和先决条件等高级功能,可精确控制changeSet的部署时间以及位置。
教程:使用 Liquibase 跟踪、版本(version)和部署数据库更改

使用 Liquibase时,可以使用 Liquibase 函数SQL 定义更改。重要的是,这些模式不是互斥的,可以结合使用,从而在如何定义和部署数据库更改方面提供了相当大的灵活性。对于使用 Liquibase 函数定义的更改,Liquibase生成适合目标数据库的SQL。这在下列场景是很有用的:

  • 支持多个不同的数据库后端。如果您是软件供应商,希望避免编写相同的数据库迁移以支持不同的数据库平台,则这是一个常见的用例。
  • 使不熟悉 SQL 或不熟悉 SQL 的开发人员能够定义数据库更改。数据库迁移可以在 XMLJSONYAML 中定义,而不是 SQL
  • 标准化数据库更改。Liquibase将生成语法和风格上一致的SQL,例如,确保所有CREATE TABLE迁移具有相同的样式和模式。

或者,Liquibase 直接与用户提供的数据库迁移脚本一起使用。这在下列场景是很有用的:

  • 进行不是 Liquibase函数的更改。自定义或特定于数据库的更改(例如,Oracle嵌套表)通常不是 Liquibase函数。
  • 使精通 SQL 的开发人员非常倾向于直接使用 SQLLiquibase只支持XML 数据库迁移,这是一个常见的误解。现实是,Liquibase 绝对可以支持普通的 SQL 脚本!

注意:Liquibase ProLiquibase函数添加了用于定义过程数据库代码的更改类型。但是,与 Liquibase 函数的其他更改不同,这些过程数据库代码更改(如Create Function)需要数据库平台特定的 SQL(例如,在 Oracle 上,更改将需要 PL/SQL)。这些新更改类型有助于从直接检查更改日志中更好地查看特定于数据库的更改。

教程设置

在尝试任何分步教程之前,请使用设置说明准备您的环境。

第1步:下载和解压Liquibase
  1. 下载Liquibase,可以从该页面下载最新的二进制文件
  2. 下载好*.zip或者*.tar.gz文件后,解压里面的内容到一个文件夹。
第2步: 安装Java
  1. Java 是必需的依赖项。如果尚未安装 Java,则安装Java

  2. 验证您是否具有可以运行的 java 版本。

    • 在命令行窗口执行java -version命令
    • 确保它可以成功运行并且显示了你安装的Java的版本
第3步:下载H2 JDBC数据库驱动
  1. 这些教程使用 H2 数据库。您需要下载 H2 JDBC 驱动程序,可在此处找到
  2. h2_.jar文件复制到您解压Liquibase *zip*.tar.gz的产生的目录中。
第4步: 设置liquibase.properties文件
  1. 教程使用CLI。虽然可以传递所有必需的参数(如JDBC驱动程序和数据库URL),但配置 liquibase.properties文件会更容易节省时间和精力。
  2. 创建一个 liquibase.properties。将以下内容添加到文件中,并将其保存在您解压Liquibase *zip*.tar.gz的产生的目录中。
driver: org.h2.Driver
classpath: ./h2-1.4.199.jar
url: jdbc:h2:file:./h2tutorial
username: admin
password: password
changeLogFile: myChangeLog.xml

注意:请务必使用复制到解压的Liquibase 目录中的h2_.jar文件的实际版本!

就这些了!现在,您已设置为开始其中一个教程!


Liquibase 教程:开始使用 SQL 脚本
第一步创建一个sql文件夹

在解压的Liquibase 的文件夹中 ,创建一个 sql 文件夹。在这个文件夹中你将放置 Liquibase将跟踪、版本和部署的SQL脚本。

第二步建立一个Change Log

这是一次性步骤,用于配置更改日志以指向将包含 SQL 脚本的 sql 文件夹。在解压的*.zip*.tar.gzLiquibase 的目录中创建并保存文件名为 myChangeLog.xml 的文件 。myChangeLog.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-3.1.xsd">

  <includeAll path="/sql"/>
</databaseChangeLog>
第3步在SQL文件中新增一个SQL脚本

使用教程设置中的 liquibase.properties 文件以及新创建的myChangeLog.xml,我们现在已准备好开始向 sql文件夹添加SQL脚本。Liquibase 将在文件夹中按字母数字顺序排列脚本。使用以下内容创建 001_create_person_table.sql 并将其保存在 sql文件夹中:

create table PERSON (
    ID int not null,
    FNAME varchar(100) not null
);
第4步 部署你的第一个修改

现在,我们已准备好部署我们的第一个脚本!打开终端,如果在 UNIX系统上则运行 ./liquibase update或 如果在 Windows 上则运行liquibase.bat update

第5步 检查你的数据库

您将看到您的数据库现在包含一个名为PERSON的表。要将作为本教程一部分的 H2 数据库写入内容,请打开一个终端,导航到您提取的 Liquibase``*.zip*.tar.gz的文件夹,并运行 java -jar h2-1.4.199.jar注意:输入您下载的 h2*.jar 的特定版本!输入JDBC URL、用户名和密码,从 liquibase.properties 文件输入您根据教程设置创建的属性文件。您会注意到还创建了另外两个表:databasechangeloglockdatabasechangeloglockdatabasechangelog表包含针对数据库运行的所有更改的列表。databasechangeloglock表用于确保两台计算机不会同时尝试修改数据库。

后续步骤
  • 此快速入门指南旨在快速向您介绍 Liquibase。有关其所有功能的完整描述,请参阅Liquibase 手册,阅读最佳实践并访问论坛

  • 如果您有一个现有项目,您正在寻找添加Liquibase,请访问现有项目页面。

  • 如果您对商业支持、培训或咨询感兴趣,请考虑 Liquibase Pro


教程:使用 Liquibase 函数入门

本教程使用Liquibase函数。更改将在 XML 中定义,而不是使用 SQLLiquibase 将根据定义的changeSet(s)生成 SQL,并将该 SQL 部署到目标数据库。所有迁移都在更改日志中显式跟踪和排序。

第一步创建一个Changelog文件

database changelog文件是列出所有数据库更改的位置。创建一个名为 myChangeLog.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-3.1.xsd">

</databaseChangeLog>
第2步 添加一个Change Set

每个changeSet都由id属性和author属性进行唯一标识。这两个标记以及更改日志文件的名称和包唯一地标识了更改。如果只需要指定一个id,则很容易意外重用它们,尤其是在处理多个开发人员和代码分支时。包括author属性可最大程度地减少重复的可能性。

将每个changeSet视为要应用于数据库的原子更改。通常最好在changeSet中只包含一个更改,但如果插入多个行时,这些行一起有作为单个事务添加的意义,则允许进行更多更改。Liquibase 将尝试将每个changeSet运行为单个事务,但对于某些命令,许多数据库将静默地提交和回滚事务(创建表、删除表等)。

<?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-3.1.xsd">

    <changeSet id="1" author="bob">
        <createTable tableName="department">
            <column name="id" type="int">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="name" type="varchar(50)">
                <constraints nullable="false"/>
            </column>
            <column name="active" type="boolean" defaultValueBoolean="true"/>
        </createTable>
    </changeSet>

</databaseChangeLog>
第3步 运行change Set

如果是在UNIX操作系统,可以使用./liquibase update命令来执行myChangeLog.xml;如果是在Windows操作系统上,可以使用liquibase.bat update来运行myChangeLog.xml

检查你的数据库

您将看到您的数据库现在包含一个名为PERSON的表。要将作为本教程一部分的 H2 数据库入内容,请打开一个终端,导航到您提取的 Liquibase``*.zip*.tar.gz的文件夹,并运行 java -jar h2-1.4.199.jar注意:输入您下载的 h2*.jar 的特定版本!输入JDBC URL、用户名和密码,从 liquibase.properties 文件输入您根据教程设置创建的属性文件。您会注意到还创建了另外两个表:databasechangeloglockdatabasechangeloglockdatabasechangelog表包含针对数据库运行的所有更改的列表。databasechangeloglock表用于确保两台计算机不会同时尝试修改数据库。

后续步骤
  • 此快速入门指南旨在快速向您介绍 Liquibase。有关其所有功能的完整描述,请参阅Liquibase 手册,阅读最佳实践并访问论坛

  • 如果您有一个现有项目,您正在寻找添加Liquibase,请访问现有项目页面。

  • 如果您对商业支持、培训或咨询感兴趣,请考虑 Liquibase Pro


Liquibase 最佳实践

本篇介绍了可以应用于项目的许多最佳实践。

组织你的changeLogs

组织changeLogs的最常见方法是按主要版本。在classpath中选择一个包来存储changeLogs,最好是在数据库访问类附近。在此示例中,我们将使用 com/example/db/changlog.

目录结构如下所示:
com
  example
    db
      changelog
        db.changelog-master.xml
        db.changelog-1.0.xml
        db.changelog-1.1.xml
        db.changelog-2.0.xml
      DatabasePool.java
      AbstractDAO.java

db.changelog-master.xml

master.xml 包括版本以正确顺序排列的changelog。在上面的示例中,它可能如下所示:

<?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-3.1.xsd">

  <include file="com/example/db/changelog/db.changelog-1.0.xml"/> 
  <include file="com/example/db/changelog/db.changelog-1.1.xml"/> 
  <include file="com/example/db/changelog/db.changelog-2.0.xml"/> 
</databaseChangeLog> 

每个包含的 XML 文件需要与标准 XML 数据库changelog的格式相同,如下所示:

<?xml version="1.0" encoding="UTF-8"?> 
<databaseChangeLog 
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
                      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd"> 
  <changeSet author="authorName" id="changelog-1.0">
    <createTable tableName="TablesAndTables">
      <column name="COLUMN1" type="TEXT">
        <constraints nullable="true" primaryKey="false" unique="false"/>
      </column>
    </createTable>
  </changeSet>
</databaseChangeLog> 

db.changelog-master.xml是您传递给所有 Liquibase 调用的changelog

管理存储过程

尝试为存储过程维护单独的changelog,并使用 runOnChange="true"。此标志强制 LiquiBase 检查changeSet是否被修改。如果是这样,liquibase 将再次执行更改。

每个changeSet只做一个更改

尽可能避免每个changeSet进行多次更改,以避免可能使数据库处于意外状态的自动提交语句失败。

ChangeSetids

选择适合您的内容。有些使用从 1 开始的序列号,在changelog中唯一,有些选择描述性名称(例如new-address-table)。

文档的ChangeSets

changeSet中使用<comments>,they say : a stitch in time saves nine

始终考虑回滚

尝试以可以回滚的方式编写changeSet。例如,使用相关的更改子句,而不是使用自定义<sql>标记。每当更改不支持开箱即用回滚时,请包含<rollback>子句。(例如<sql><insert>等)

参考数据管理

利用 Liquibase 管理您的参考数据。环境分离(DEVQAPROD)可以使用context实现。

开发人员的过程
  • 使用您最喜爱的IDE 或编辑器,创建一个新的本地changeSet包含了更改。

  • 运行 Liquibase 以执行新的changeSet(这将测试 SQL 代码);

  • 对应用程序代码执行相应的更改(例如,Java 代码);

  • 测试新的应用程序代码以及数据库更改;

  • 提交changeSet和应用程序代码。

考虑Datical
  • Datical 是一种基于 Liquibase 核心功能的商业产品。除了版本控制和管理数据库更改之外,Datical 还通过启用数据库代码完全统一和自动化路径的功能来弥合开发和操作之间的差距。

  • Datical 具有 Web 界面、命令行界面和 REST API。所有接口都是安全的,需要身份验证。

  • Datical根据组织标准自动验证数据库代码,以消除手动审核

  • Datical 自动从经过验证的 DDL代码生成changeSet,从而消除了在编写changeSet和手动更新changeSet时手动工作。

  • Datical 为数据库代码生成一个不可变的项目,用于一致、可重复和自动化就绪的下游部署

  • Datical 使用目标数据库的基于对象的模型预测数据库更改的影响,以确保在部署数据库更改时,Datical 与票务系统(如 JIRA)集成时没有错误或问题,以便轻松地将数据库更改回溯到源代码和初始要求。此功能还便于保存或加速功能集

  • Datical 可以快照和比较数据库架构,以帮助识别和address drift

想了解更多关于Datical的信息, 你可以参考该网站 http://www.datical.com/Liquibase


Liquibase的选择

对于数据库源代码管理需求,有两种不同的选项可供选择。

1. Liquibase社区版本

开源数据库版本控制。Liquibase 为团队提供了一个很好的起点,以应对管理数据库更改带来的挑战。它比推送数据库脚本的作用更大,它还生成和部署它们。

社区版本下载链接

2.Liquibase企业版

虽然 Liquibase社区是一个很好的起点,但希望如何充分利用Liquibase的最佳实践,以及随着解决方案的扩展而提高可见性和控制力的团队可能会发现 Liquibase Pro 更适合他们的需求。Liquibase Pro 增加了增强和扩展基本开源功能的能力,例如添加函数以更改用于更新过程数据库代码的集。非试用 Liquibase Pro许可证附带支持,因此您不必单独使用。免费试用14天。无需信用卡。

Liquibase Pro版本收费目录


核心概念
Changelog 文件

开发人员将数据库更改存储在其本地开发计算机上基于文本的文件中,并将其应用于其本地数据库。Changelog文件可以任意嵌套,以便更好地管理。

更多

ChangeSet

changeSetauthorid属性以及changelog文件的位置唯一标识,是 Liquibase 跟踪执行的单位。运行 Liquibase 时,它会查询标记为已执行的changSetDATABASECHANGELOG 表,然后执行更改日志文件中尚未执行的所有changeSet

更多

Changes

更改每个changeSet通常包含一个更改,该更改描述要应用于数据库的更改/重构。Liquibase 支持为支持的数据库和原始 SQL 生成 SQL 的描述性更改。通常,每个changeSet应只有一个更改,以避免可能使数据库处于意外状态的自动提交语句失败。

更多

Preconditions

先决条件可以应用于整个changelog或单个changeSet。如果先决条件失败,liquibase将停止执行。

Contexts

可以将上下文应用于changeSet,以控制在不同环境中运行的changeSet。例如,某些changeSet可以标记为production,另一些可以标记为test。如果未指定上下文,则无论执行上下文如何,changset都将运行。

更多


数据库Change Log文件

所有 Liquibase 更改的根目录是databaseChangeLog文件。

可用属性

logicalFilePath:用于在创建changeSet的唯一标识符时覆盖文件名和路径。移动或重命名change logs时是必需的。

可用的子标签
  • preConditions:执行更改日志所需的先决条件。read more
  • property:将属性设置为的值(如果不是通过其他方法设置)。read more
  • changeSet:要执行的changeSetread more
  • include:包含要执行的changeSet的其他文件。read more

Liquibase 迁移器运行时,它将分析数据库 ChangeLog 标记。它首先检查指定的先决条件。如果先决条件失败,Liquibase将退出,并显示一条错误消息,解释失败的原因。先决条件对于记录和强制执行更改日志编写器的预期和假设(如要针对的 DBMS 或以用户身份运行更改)非常有用。

如果满足所有的先决条件,Liquibase将会开始运行在databaseChangeLog文件中按照顺序出现changeSetinclude标签。

databaseChangeLog标签的XML模式在3.1就可以使用:

dbchangelog-3.1.xsd

某些旧版 XSD 列在XML Format page

每个changeSet包含一个id标签和一个author标签。这些标签以及类路径位置和 XML 文件的名称为该更改集创建唯一标识符。

示例:空的change log

XML Format

<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
    http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
</databaseChangeLog>

YAML Format

databaseChangeLog:

JSON Format

{
    "databaseChangeLog": [
    ]
}

SQL Format

--liquibase formatted sql

可以附加preConditionsdatabaseChangeLogchangeSet,以控制基于数据库状态的更新的执行。

下面是使用preConditions的几个原因:

  • 记录更改日志的编写者在创建changelog时的假设。
  • 强制使运行change log的用户不会违反这些假设
  • 在执行不可恢复的更改(如 drop_Table)之前执行数据检查
  • 根据数据库的状态控制哪些changeSet运行

如果需要,preConditions可以是<changeSet>中的唯一标记。

databaseChangeLog级别的preConditions适用于所有changeSet,而不仅仅是当前changeLog或其子changelog中列出的changeset

下面是简单使用preConditions的示例
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.8"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.8
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.8.xsd">
    <preConditions>
        <dbms type="oracle" />
        <runningAs username="SYSTEM" />
    </preConditions>

    <changeSet id="1" author="bob">
        <preConditions onFail="WARN">
            <sqlCheck expectedResult="0">select count(*) from oldtable</sqlCheck>
        </preConditions>
        <comment>Comments should go after preCondition. If they are before then liquibase usually gives error.</comment>
        <dropTable tableName="oldtable"/>
    </changeSet>
</databaseChangeLog>

仅当针对 Oracle执行的数据库和执行脚本的数据库用户为SYSTEM时,才会运行上述databasechangelog。仅当"oldtable"中没有值时,它才会运行 drop_Table命令。

处理失败和错误

自 1.8 版本起,Liquibase 区分了preConditions的"失败"(检查失败)和"错误"(在执行检查时引发的异常),并且可以通过<preConditions>标签上的onFailonError属性控制对两者的反应。

可用的属性
  • onFail: 当proConditions遇到失败的时候如何处理
  • onError:当proConditions遇到错误的时候如何处理
  • onUpdateSQL:自版本1.9.5后当proConditions遇到更新SQL模型的时候如何处理
  • onFailMessage:自2.0起,在proConditions失败时要输出的自定义消息。
  • onErrorMessage:在proConditions错误时要输出的自定义消息。
onFail或者onError可能的取值
  • HALT:立即停止执行整个changelog。默认的值。
  • CONTINUE:跳过changeset。将在下次更新时再次尝试执行changeset。继续changelog
  • MARK_RAN:跳过changeset,但将其标记为已执行。继续changelog
  • WARN:输出警告并继续正常执行changeset/changelog

changset之外(例如,在changelog的开头),可能的值只有 HALTWARN

onUpdateSQL可能的值
  • RUN:在upateSQL的模式下运行changeSet
  • FAIL:在updateSQL的模式下运行preConditions
  • IGNORE:在updateSQL的模式下忽略preConditions
AND/OR/NOT逻辑

可以使用可嵌套<and><or><not>标签将条件逻辑应用于preConditions。如果未指定条件标签,则默认为 <AND>

例子:

 <preConditions onFail="WARN">
     <dbms type="oracle" />
     <runningAs username="SYSTEM" />
 </preConditions>

将检查更新是否运行在Oracle上,并且用户是SYSTEM,但是如果preConditions失败,它将只会生成警告信息。

 <preConditions>
     <dbms type="oracle" />
     <dbms type="mysql" />
 </preConditions>

要求运行在OracleMySQL上,这总是虚假的,除非发生大规模和意外的合并。

<preConditions>
     <or>
         <dbms type="oracle" />
         <dbms type="mysql" />
     </or>
 </preConditions>

需要在OracleMySQL 上运行,这比上面的示例更有意义。

 <preConditions>
     <or>
         <and>
            <dbms type="oracle" />
            <runningAs username="SYSTEM" />
         </and>
         <and>
            <dbms type="mssql" />
            <runningAs username="sa" />
         </and>
     </or>
 </preConditions>

如果对 Oracle 数据库执行,则需要以 SYSTEM身份运行;如果对MS-SQL 数据库运行,则需要以 SA身份运行。

可用的preConditions

<dbms>:

  • 如果针对所执行的数据库与指定的类型匹配,则通过。
  • type:预期的数据库类型。可以使用逗号分隔值指定多个 dbms 值。必填

<runningAs>

  • 如果执行的数据库用户与指定的用户名匹配,则通过。
  • username:数据库用户脚本应以原样运行。必填

<columnExists>

  • 从1.8开始如果数据库中存在具体的列,则通过
  • schemaName:表的schema的名称。必填
  • tableName:列表的名称。必填
  • columnName:列名称。必填

<tableExists>

  • 从1.8开始,如果数据库中存在具体的表,则通过
  • schemaName:表的schema的名称。必填
  • tableName:表的名称。必填

<viewExists>

  • 从1.8开始,如果数据库中存在具体的视图,则通过
  • schemaName:视图的schema的名称。必填
  • viewName:视图的名称。必填

<foreignKeyConstrainExists>

  • 从1.8开始,如果数据库存在指定的外键,则通过
  • schemaName:外键的schema名称,必填
  • foreignKeyName:外键的名称。必填

<indexExists>

  • 从1.8开始,如果数据库存在指定的索引,则通过
  • schemaName:索引的schema名称,必填
  • indexName:索引名称,必填

<sequenceExists>

  • 从1.8开始,如果数据库存在指定的序列,则通过
  • schemaName:序列的schema名称,必填
  • sequenceName:序列的名称,必填

<primaryKeyExists>

  • 从1.8开始,如果数据库中存在指定的主键,则通过
  • schemaName:主键的schema名称
  • primaryKeyName:主键的名称,表名或者主键名是必填
  • tableName:包含主键的表的名称。从1.9开始表名或者主键名是必填

<sqlCheck>

  • 执行 SQL 字符串并检查返回的值。SQL 必须返回具有单个值的单个行。要检查行数,请使用SQL 函数count。要检查值范围,请在 SQL 中执行检查,并返回一个可以容易比较的值。

  • <sqlCheck expectedResult="1">
        SELECT COUNT(1) FROM pg_tables      WHERE TABLENAME = 'myRequiredTable'
    </sqlCheck>
    
  • expectedResult:这个值与SQL的执行结果作比较,必填

<changeLogPropertyDefined>

  • 检查是否存在给定的changelog参数。如果还给定了值,则仅当该值与给定值不同时,该值才会失败。

  • 从2.0开始

  • <changeLogPropertyDefined property="myproperty"/>
    <changeLogPropertyDefined property="myproperty" value="requiredvalue"/>
    
  • property:要检验的属性的名称,必填

  • value:给定属性的必需值。

<customPrecondition>:

  • 可以通过创建实现 liquibase.precondition.CustomPrecondition接口的类来创建自定义precondition。自定义类上的参数通过基于<param>子标签的反射进行设置。参数作为字符串传递到自定义preCondition

  • <customPrecondition className="com.example.CustomTableCheck">
        <param name="tableName" value="our_table"/>
        <param name="count" value="42"/>
    </customPrecondition>
    
  • className:custom precondition类的名称。必填

  • 子标签

    • param:传递给custom precondition的参数
      • param子标签属性:
        • name:要设置的参数的名称。必填
        • value:要将参数设置为的字符串值。必填
实施说明

preConditions在执行特定changelogs开始时进行检查。如果使用include标签,并且仅在子changelog上具有preConditions,则在迁移器到达该文件之前不会检查这些preConditions。此行为可能会在将来的版本中更改,因此不要依赖此行为。


Drop Table

删除已经存在的表

可用的属性
  • cascadeConstraints: 支持所有
  • catalogName:支持所有;catalog的名称;从3.0开始有该属性
  • schemaName:支持所有;schema的名称
  • tableName:支持所有;要删除的表的名称;

XML 例子

<changeSet author="liquibase-docs" id="dropTable-example">
    <dropTable cascadeConstraints="true"
            catalogName="cat"
            schemaName="public"
            tableName="person"/>
</changeSet>

YAML例子

changeSet:
  id: dropTable-example
  author: liquibase-docs
  changes:
  - dropTable:
      cascadeConstraints: true
      catalogName: cat
      schemaName: public
      tableName: person

JSON例子

{
  "changeSet": {
    "id": "dropTable-example",
    "author": "liquibase-docs",
    "changes": [
      {
        "dropTable": {
          "cascadeConstraints": true,
          "catalogName": "cat",
          "schemaName": "public",
          "tableName": "person"
        }
      }]
    
  }
}
从上面的示例生成 SQLMySQL
DROP TABLE cat.person;
支持的数据库

图片liquibase0002


Change Log 参数

从Liquibase 1.7开始

Liquibase 允许在changelog中动态替换参数。使用 ${} 语法描述要替换的参数。

配置参数的值

参数的值会被按照下面的顺序进行查找

  • 作为参数传递给您的 Liquibase 运行程序(请参阅 Antcommand等文档,了解如何传递它们)ant和command没有了解
  • 作为 JVM系统属性
  • 在数据库ChangeLog文件本身的参数块(<property>标签)中
  • 作为环境变量

例子:

<createTable tableName="${table.name}">
     <column name="id" type="int"/>
     <column name="${column1.name}" type="varchar(${column1.length})"/>
     <column name="${column2.name}" type="int"/>
</createTable>
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">

    <property name="clob.type" value="clob" dbms="oracle"/>
    <property name="clob.type" value="longtext" dbms="mysql"/>

    <changeSet id="1" author="joe">
         <createTable tableName="${table.name}">
             <column name="id" type="int"/>
             <column name="${column1.name}" type="${clob.type}"/>
             <column name="${column2.name}" type="int"/>
         </createTable>
    </changeSet>
</databaseChangeLog>
<property>

changelog定义一个参数。给定上下文and/or数据库的列表,该参数将仅用于这些上下文and/or数据库。

可用属性
  • name:表schema的名称;必填
  • value:列表的名称;必填
  • context:以逗号分隔列表表示的上下文。
  • dbms:作为逗号分隔列表给出的数据库类型。
  • global:定义属性是全局的还是仅限于数据库ChangeLog的。以truefalse表示。

例子:

<property name="simpleproperty" value="somevalue"/>
<property name="clob.type" value="clob" dbms="oracle,h2"/>
<property name="clob.type" value="longtext" dbms="mysql"/>
<property name="myproperty" value="yes" context="common,test"/>
<property name="localproperty" value="foo" global="false"/>

<changeSet>标签

changeSet标签是你用来分组数据库更改的。refactoring

<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd">
    <changeSet id="1" author="bob">
        <comment>A sample change log</comment>
        <createTable/>
    </changeSet>
    <changeSet id="2" author="bob" runAlways="true">
        <alterTable/>
    </changeSet>
    <changeSet id="3" author="alice" failOnError="false" dbms="oracle">
        <alterTable/>
    </changeSet>
</databaseChangeLog>

每个changeSet标签都由id标签、author标签和changelogclasspath名称的组合唯一标签。id 标签仅用作标识符,它不指示更改运行的顺序,甚至不一定是整数。如果您不知道或不希望保存实际作者,只需使用占位符值,如UNKNOW

Liquibase执行数据库ChangeLog时,它按顺序读取changeSet,并针对每个changeSet检查databasechangelog表,以查看是否运行了 id/author/filepath的组合。如果已运行,则将跳过changeSet,除非存在真正的runAlways标签。运行changeSet中的所有更改后,Liquibase 将在databasechangelog中插入带有 id/author/filepath的新行以及changeSetMD5Sum(见下文)。

Liquibase 尝试执行每个changeSet并在每次结束时提交事务,或者如果出现错误,则回滚。某些数据库将自动提交语句,这些语句会干扰此事务设置,并可能导致意外的数据库状态。因此,通常最好每个changeSet只进行一次更改,除非有一组非自动提交更改要应用为事务(如插入数据)。

可用属性
  • id:字母数字标识符,必须
  • author:创建changeSet的人,必须
  • dbms:要用于changSet的数据库的类型。运行迁移步骤时,它会根据此属性检查数据库类型。有效的数据库类型名称列在受支持的数据库页上
  • runAlways:执行每次运行时设置的更改,即使更改之前已运行
  • runOnChange:在第一次看到更改时以及每次更改集更改时执行更改
  • context:如果在运行时传递了特定上下文,则执行更改。任何字符串都可用于上下文名称,并且它们处于不区分大小写状态。
  • runInTransaction:changeSet是否应作为单个事务运行(如果可能)?默认值为 true。从1.9开始,警告:小心使用此属性。如果设置为 false,并且通过运行包含多个语句的 changeSet部分发生错误,则 Liquibase 数据库更改日志表将保持无效状态。
  • failOnErroe:如果在执行changeSet时发生错误,是否认为此迁移失败?
可用的子标签
  • comment:changeSet的说明。XML 注释将提供相同的好处,Liquibase 的未来版本可能能够利用<comment>标记注释来生成文档
  • preConditions:将执行changeSet之前必须通过的前提条件。可用于在做不可恢复的内容(如自 1.7 起删除表)之前执行数据健全性检查
  • <AnyRefactoringTag(s)>:作为此changeSet的一部分运行的数据库更改(称为重构)
  • validCheckSum:列出被认为对此更改有效的校验,而不考虑数据库中存储的内容。自 1.7 起,主要用于需要修改changeSet,并且不希望在已运行过此修改的数据库上引发错误(不是建议的步骤)。
  • rollback:描述如何回滚changeSet的 SQL 语句或重构标签
Rollback标签

回滚标记描述如何对 SQL 语句、change tag或以前的changeSet的引用进行回滚更改。

回滚标签的例子:

<changeSet id="1" author="bob">
    <createTable tableName="testTable">
    <rollback>
        drop table testTable
    </rollback>
</changeSet>
<changeSet id="1" author="bob">
    <createTable tableName="testTable">
    <rollback>
        <dropTable tableName="testTable"/>
    </rollback>
</changeSet>
<changeSet id="2" author="bob">
    <dropTable tableName="testTable"/>
    <rollback changeSetId="1" changeSetAuthor="bob"/>
</changeSet>
ChangeSet校验总结

Liquibase 达到 changeSet 时,它会计算一个校验和并将其存储在databasechangelog中。存储校验总和的值是使 Liquibase 可以知道是否有人更改了自运行以来更改了changeSet。如果changeSet自运行以来发生更改,Liquibase 将退出迁移并报错,因为它无法知道更改了哪些内容,并且数据库的状态可能与changelog所期望的状态不同。如果changeSet有正当理由已更改,并且要忽略此错误,请更新databasechangelog表,以便具有相应 id/author/filepath的行具有校验总值的空值。下次 Liquibase 运行时,它将将校验总和值更新为新的正确值。

校验和还与runOnChange changeSet属性结合使用。有时您可能不希望添加新的changSet,因为您只需了解当前版本,但您希望在更新此更改时应用此更改。存储过程是何时需要此方法的一个好示例。如果每次进行更改时将存储过程的整个文本复制到新的changeSet,您不仅会获得很长的changelog,而且还会失去源代码管理的融合和分散功能。而是将存储过程的文本放在具有 runOnChange="true"属性的changeSet中。仅当存储过程的文本发生更改时,才会重建存储过程。


回滚ChangeSet

Liquibase 允许您自动或通过自定义回滚 SQL 撤消对数据库所做的更改。回滚支持在command_lineAntMaven中可用。Maven没有看

如何控制回滚 SQL

许多重构(如create tablerename columnadd column)可以自动创建回滚语句。如果您的changeLog仅包含适合此类别的语句,则将自动生成回滚命令。

其他重构(如drop tableinsert data)没有可自动生成的相应回滚命令。在这些情况下,如果要覆盖默认生成的回滚命令,可以通过changeSet标签中的标签指定回滚命令。如果不希望执行任何操作来撤消回滚模式下的更改,请使用空标签。

可用的回滚标签属性
  • nested tags:标准的Liquibase更改标签来生成回滚语句
  • tag text:使用sql去运行回滚更改
  • changeSetId:要重新运行的changeSetID 以回滚此更改。示例:要回滚 Drop Table 更改,请参阅创建表的changeSet
  • changeSetAuthor:要重新运行的changeSet的作者,以便回滚此更改
样例

很多change标签是不需要rollback标签的,因为通过update语句,他们可以自动生成。

<changeSet id="changeRollback2-create" author="nvoxland">
     <createTable tableName="changeRollback2">
         <column name="id" type="int"/>
     </createTable>
</changeSet>

标准的change标签可以被使用<rollback>标签

   <changeSet id="changeRollback" author="nvoxland">
        <createTable tableName="changeRollback1">
            <column name="id" type="int"/>
        </createTable>
        <rollback>
            <dropTable tableName="changeRollback1"/>
        </rollback>
    </changeSet>

多个语句可以被包含在一个<rollback>标签中。一个changeSet中可以有多个rollback标签

<changeSet id="multiRollbackTest" author="rs">
    <createTable tableName="multiRollback1">
        <column name="id" type="int"/>
    </createTable>
    <createTable tableName="multiRollback2">
        <column name="id" type="int"/>
    </createTable>
    <createTable tableName="multiRollback3">
        <column name="id" type="int"/>
    </createTable>
    <rollback>
        drop table multiRollback1;
        drop table multiRollback2;
    </rollback>
    <rollback>drop table multiRollback3</rollback>
</changeSet>

rollback标签可以引用更改设置最初创建的语句

<changeSet id="changeRollback2-drop" author="nvoxland">
     <dropTable tableName="changeRollback2"/>
     <rollback changeSetId="changeRollback2-create" changeSetAuthor="nvoxland"/>
</changeSet>

如果无法/需要回滚,回滚标记可以为空

<changeSet id="noRollback" author="nvoxland">
    <sql>insert into multiRollback3 (id) values (1)</sql>
    <rollback/>
</changeSet>
Roll Back to模式

您可以通过以下三种方式指定要回滚的更改:

  • Tag:指定要回滚到的标签将回滚标签后针对目标数据库执行的所有changeSet。有关如何标记数据库,请参阅command line文档。
  • change Sets的数量:您可以指定要回滚的changeSet数。
  • Date:您可以指定要回滚到的日期。
回滚执行模式

Liquibase 有三种管理回滚的模式:

  • 直接执行回滚:回滚命令可以直接针对目标数据库执行。在无法回滚任何更改时,您将会收到通知,并且不会回滚任何更改。
  • 生成一个回滚脚本:与实际更新数据库相比,可以生成回滚数据库所需的 SQL。在实际运行之前,如果要预览将执行哪些回滚命令,此模式将非常适合。
  • 生成一个“未来回滚”脚本:此模式旨在允许您在生成迁移脚本的同时生成回滚脚本。它允许您采用更新的应用程序,并生成 SQL 以将数据库更新到新版本,以及在需要时生成 SQL 以将新版本恢复到当前版本。当 DBA希望控制 SQL进入数据库时,以及对于需要内部and/or SOX-compliant流程的回滚文档的应用程序,此功能非常有用。在此模式下,不需要指定回滚日期、标记或计数。

XML格式化

Liquibase 支持 XML作为存储changelog文件的格式。

XSD支持

XSD架构定义可用于每个 Liquibase 版本。由于修补程序版本中没有changelog格式,因此只有 xsd 文件对应于主要、次要版本。

  • http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
  • http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd
  • http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd
  • http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd
  • http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.8.xsd
Liquibase的扩展XSDs

如果您使用的是包含其他更改标签的 Liquibase 扩展,请查看扩展文档以了解它们是否提供了 XSD。如果不这样做,则可以使用 xsd 在http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd允许任何嵌套标记和属性。

限制

例子
<?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"
        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">

    <preConditions>
        <runningAs username="liquibase"/>
    </preConditions>

    <changeSet id="1" author="nvoxland">
        <createTable tableName="person">
            <column name="id" type="int" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="firstname" type="varchar(50)"/>
            <column name="lastname" type="varchar(50)">
                <constraints nullable="false"/>
            </column>
            <column name="state" type="char(2)"/>
        </createTable>
    </changeSet>

    <changeSet id="2" author="nvoxland">
        <addColumn tableName="person">
            <column name="username" type="varchar(8)"/>
        </addColumn>
    </changeSet>
    <changeSet id="3" author="nvoxland">
        <addLookupTable
            existingTableName="person" existingColumnName="state"
            newTableName="state" newColumnName="id" newColumnDataType="char(2)"/>
    </changeSet>
</databaseChangeLog>

YAML 格式化

Liquibase 支持 YAML 作为存储changelog文件的格式。

要求

要使用基于 YAML 的更改日志,必须在类路径中包括 snakeyaml-1.12.jar

限制

例子
databaseChangeLog:
  - preConditions:
    - runningAs:
        username: liquibase

  - changeSet:
      id: 1
      author: nvoxland
      changes:
        - createTable:
            tableName: person
            columns:
              - column:
                  name: id
                  type: int
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: firstname
                  type: varchar(50)
              - column:
                  name: lastname
                  type: varchar(50)
                  constraints:
                    nullable: false
              - column:
                  name: state
                  type: char(2)

  - changeSet:
      id: 2
      author: nvoxland
      changes:
        - addColumn:
            tableName: person
            columns:
              - column:
                  name: username
                  type: varchar(8)

  - changeSet:
      id: 3
      author: nvoxland
      changes:
        - addLookupTable:
            existingTableName: person
            existingColumnName:state
            newTableName: state
            newColumnName: id
            newColumnDataType: char(2)
JSON 格式化

Liquibase 支持JSON作为存储changelog文件的格式。

要求

要使用基于 JSON的更改日志,必须在类路径中包括 snakeyaml-1.12.jar

限制

例子
{
    "databaseChangeLog": [
        {
            "preConditions": [
                {
                    "runningAs": {
                        "username": "liquibase"
                    }
                }
            ]
        },

        {
            "changeSet": {
                "id": "1",
                "author": "nvoxland",
                "changes": [
                    {
                        "createTable": {
                            "tableName": "person",
                            "columns": [
                                {
                                    "column": {
                                        "name": "id",
                                        "type": "int",
                                        "autoIncrement": true,
                                        "constraints": {
                                            "primaryKey": true,
                                            "nullable": false
                                        },
                                    }
                                },
                                {
                                    "column": {
                                        "name": "firstname",
                                        "type": "varchar(50)"
                                    }
                                },
                                {
                                    "column": {
                                        "name": "lastname",
                                        "type": "varchar(50)",
                                        "constraints": {
                                            "nullable": false
                                        },
                                    }
                                },
                                {
                                    "column": {
                                        "name": "state",
                                        "type": "char(2)"
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        },

        {
            "changeSet": {
                "id": "2",
                "author": "nvoxland",
                "changes": [
                    {
                        "addColumn": {
                            "tableName": "person",
                            "columns": [
                                {
                                    "column": {
                                        "name": "username",
                                        "type": "varchar(8)"
                                    }
                                }
                           ]
                        }
                    }
                ]
            }
        },

        {
            "changeSet": {
                "id": "3",
                "author": "nvoxland",
                "changes": [
                    {
                        "addLookupTable": {
                            "existingTableName": "person",
                            "existingColumnName":"state",
                            "newTableName": "state",
                            "newColumnName": "id",
                            "newColumnDataType": "char(2)",
                        }
                    }
                ]
            }
        }
    ]
}

格式化SQL changelogs

Liquibase 2.0 起,Liquibase 包括支持普通 SQL changelog文件。这些changelog可能包含在 XMLchangelog中,并且可能包含任意 SQL 语句。语句将转换为自定义 sql 重构。

格式化的 SQL 文件使用注释为 Liquibase 提供元数据。每个 SQL 文件必须以以下注释开头:

--liquibase formatted sql
ChangeSets

格式化的 SQL 文件中的每个changeSet都以注释形式开头

--changeset author:id attribute1:value1 attribute2:value2 [...]

changeset注释后跟一个或多个 SQL 语句,用分号(或<endDelimiter>属性的值)分隔。

可用的changeset属性

每个changeSet上可能提供以下属性:

  • scriptComments: 设置为true可以在sql执行之前移除所有注释,false则相反,默认为true。
  • splitStatements:设置为false时,在“s”和“go”上不会使用Liquibase 拆分语句,默认为true。
  • endDelimiter:设置语句结尾的分隔符。默认为";“可以设置为”"
  • runAlways:每次运行的时候都执行此changeSet,即使之前执行过。
  • runOnChange:在第一次看到更改时以及每次更改集更改时执行更改
  • context:如果在运行时传递了特定上下文,则执行更改。任何字符串都可用于上下文名称,并且它们处于不区分大小写状态。
  • logicalFilePath:用于在创建changeSet的唯一标识符时覆盖文件名和路径。移动或重命名change logs时是必需的。 --和下方重复,有点问题
  • labels:labels是将changeSet分到context的通用方法,但是与在运行时定义一组context,然后在changeSet中定义一个匹配表达式相反,是定义好context的一组labels后运行时匹配对应表达式。
  • runInTransaction:changeSet是否应作为单个事务运行(如果可能的情况)?默认为true。警告:注意这个属性。如果设置为false,并且在运行包含多个语句的changeSet的过程中发生错误,Liquibase DatabaseChangeLog表将使它们处于无效状态。
  • failOnError:如果在执行变更集时发生错误,是否认为此迁移失败?
  • dbms:要用于changSet的数据库的类型。运行迁移步骤时,它会根据此属性检查数据库类型。有效的数据库类型名称列在受支持的数据库页上
  • logicalFilePath:
Preconditions

可以为每个changeSet指定preConditions。目前,仅支持 SQL检查preConditions

--preconditions onFail:HALT onError:HALT
--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM my_table
回滚行为

changset可能包括回滚changeSet时要应用的语句。回滚语句是窗体的注释

--rollback SQL STATEMENT
简单的Change Log
--liquibase formatted sql

--changeset nvoxland:1
create table test1 (
    id int primary key,
    name varchar(255)
);
--rollback drop table test1;

--changeset nvoxland:2
insert into test1 (id, name) values (1, ‘name 1);
insert into test1 (id, name) values (2, ‘name 2);

--changeset nvoxland:3 dbms:oracle
create sequence seq_test;

Custom SQL

sql标签允许您指定所需的任何 sql。它对于 Liquibase 的自动重构标记不支持的复杂更改非常有用,并且可用于解决 Liquibase 的错误和限制。sql 标签中包含的 SQL 可以是多行的。

创建过程重构是创建存储过程的最佳方式。

sql标签还可以支持同一文件中的多行语句。在 SQL 的最后一行的末尾,语句可以使用 ;或者go拆分 。多行 SQL 语句也受支持,并且仅支持 ;或go语句结束,另起新行是不够的。包含单个语句的文件不需要使用;或者go。

sql 更改还可以包含以下任一格式的注释:

多行注释可以以/*开始,以*/结束。一个单行注释可以以<space>开头在语句的末尾以<space>结束;注意:默认的它企图在语句的末尾拆分语句按照;或者go。因此,如果您有注释或其他非语句结尾;go,不要在行尾有它,否则您将获得无效的 SQL

可用的属性
  • comment:
  • dbms:
  • endDelimiter:要应用于语句末尾的分隔符。默认值为;,可以设置为''
  • splitStatement:设置为 false,以上没有 iquibase 拆分语句;GO。如果未设置,则默认值为 true
  • sql:
  • stripComments:设置为 true以在执行之前删除 SQL中的任何注释,否则为 false。如果未设置,则默认值为false

XML 例子

<changeSet author="liquibase-docs" id="sql-example">
    <sql dbms="h2, oracle"
            endDelimiter="\nGO"
            splitStatements="true"
            stripComments="true">insert into person (name) values ('Bob')
        <comment>What about Bob?</comment>
    </sql>
</changeSet>

YAML 例子

changeSet:
  id: sql-example
  author: liquibase-docs
  changes:
  - sql:
      comment: What about Bob?
      dbms: h2, oracle
      endDelimiter: \nGO
      splitStatements: true
      sql: insert into person (name) values ('Bob')
      stripComments: true

JSON 例子

{
  "changeSet": {
    "id": "sql-example",
    "author": "liquibase-docs",
    "changes": [
      {
        "sql": {
          "comment": "What about Bob?",
          "dbms": "h2, oracle",
          "endDelimiter": "\\nGO",
          "splitStatements": true,
          "sql": "insert into person (name) values ('Bob')",
          "stripComments": true
        }
      }]
    
  }
}

从上面的示例生成SQLMySQL

insert into person (name) values ('Bob')
GO
数据库支持

图片Liquibase0003


其它形式的changelog

除了内置的XMLYAMLJSONSQL changelog格式外,Liquibase扩展系统还允许您以任何你喜欢的格式创建changelog文件。

其他社区管理的格式包括:


Contexts

Liquibase 中的context是可以添加到changeSet 的标签,以控制将在任何特定的迁移运行中执行哪些设置。任何字符串都可用于上下文名称,并且它们处于不区分大小写状态。

通过任何可用方法运行迁移器时,可以传递一组要运行的context。将仅运行使用已传递上下文标记的changeset

在你迁移的时候如果没有指定一个具体的context,那么所有的context都会执行。

下面是一个在change标签中使用context属性的例子:

<changeSet id="2" author="bob" context="test">
        <insert tableName="news">
            <column name="id" value="1"/>
            <column name="title" value="Liquibase 0.8 Released"/>
        </insert>
        <insert tableName="news">
            <column name="id" value="2"/>
            <column name="title" value="Liquibase 0.9 Released"/>
        </insert>
    </changeSet>
Context语法

context语法具体是使用AND,OR,!和圆括号。在没有圆括号的情况下,操作的顺序是!,AND,OR

例子

  • context="!test"
  • context="v1.0 or map"
  • context="v1.0 or map"
  • context="!qa and !master"

使用,来分隔上下文的工作方式类似于 OR 操作,但具有最高的优先级。

例子

  • "test,qa"等价于test OR qa
  • "test,qa and master"等价于test OR (qa AND master)

有效性

  • Liquibase的所有版本中,分隔符都是可用的
  • 在3.2.0版本中新增了AND,OR,!和圆括号
使用上下文测试数据

如果您使用 Liquibase 管理测试数据,最佳方式是与所有其他changeSet放在一起,但是加上标有testcontext。这样,当您希望插入测试数据时,可以使用contexttest运行迁移器。在迁移生产数据库时,不要包含context='test',并且不会包含测试数据。如果您有多个测试环境或测试数据集,只需使用不同的上下文(如mini-testintegration-test等)标记它们即可。
使用上下文控制测试数据比使用单独的changelog树要好,因为重构和更改后需要使用现有的测试数据,就像它们也会使用生产数据一样。如果有一组测试数据是在数据库建立后创建并简单添加的,那么您需要不断地手动更新测试数据脚本,以使它们与当前数据库模式保持一致。

使用上下文进行多 DBMS 更改日志

可以使用上下文来控制哪些changeSet在哪些数据库上运行,但更好的选择是在changeSet 标记上使用内置的dbms标记。

默认的上下文

Liquibase 3.5 开始,您可以在databaseChangeLog节点中指定context属性,以在默认情况下将该上下文分配给changelog中的所有changeSet
特别的context需要在文件中的changeSet中特别指定,它会已AND的形式接在context后面。

include/includeAll 上下文

Liquibase 3.5 开始,您可以指定一个context属性使用 or 标签。如果指定,则给定的context将添加到包含文件中的所有changeSet


includeAll标签允许你将你的change-logs分解为很多个来管理,它和include标签很类似,但是不是传递一个特定的changelog文件来包含,你指定一个目录,它将会把这个目录中的所有*.xml文件当做changelog文件,所有的*.sql文件作为单独的更改。所有被发现的文件都会按照字母顺序依次执行的。

例子
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
    <includeAll path="com/example/changelogs/"/>
</databaseChangeLog>
警告

虽然 includeAll 标签有许多有价值的用途,但其使用可能会导致问题。要避免的最大事情是使用 includeAll 标签来模拟按文件顺序运行的更改列表的 Ruby 活动迁移策略,每个文件一个。虽然这看起来是个好主意,但是它很快就会运行出现问题

如果选择使用 includeAll 标记,请确保有一个命名策略,以确保您永远不会发生冲突,或者需要重命名文件以强制重新排序


include 标签

include标签允许您将 change-logs分解为几个更易于管理的部分。如果需要更容易地包含多个文件,请使用includeall标记。

<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
    <include file="com/example/news/news.changelog.xml"/>
    <include file="com/example/directory/directory.changelog.xml"/>
</databaseChangeLog>

随着项目的增长,changelog中的changesets数量可能会变得难以操作。为了帮助缓解此问题,并使更改管理更容易,可以包括数据库changelog来创建changelog树。在上面的示例中,根changelog首先包括 com/example/news/news.changelog.xml 中的更改,然后包括 com/example/directory/directory.changelog.xml 中的更改。

包含的changelog,因此确实需要小心谨慎,以确保包含的changelog是完全独立的,或者确保首先运行任何必需的changelog

在运行任何changeSet之前,将考虑在子changelog文件中的changelog级别定义的任何preConditions

使用<include>标签而不是使用 XML 的内置包含功能的原因是,使用内置功能,解析器只能看到一个大型 XML 文档。我们使用 idauthorfilename唯一标识每个change,因此您只需要确保每个文件中的 id/author组合是唯一的,而不是跨所有changelog

可用的属性
  • file:要include进来的文件的名称,必填
  • relativeToChangelogFile:从1.9开始,是相对于根changelog文件而不是类路径的文件路径。默认值为false
实施说明

检查是否有循环changelog或双包含changelog

如果包含两次changelog,则不是问题,因为第二次,Liquibase 会知道该changeset已运行,并且不会再次运行它们(即使有运行始终标记)。

如果创建一个changelog循环 (root.changelog.xml包含news.changelog.xml,其中包括 root.changelog.xml),您将获得无限循环。检查循环是我们增强功能列表中的一项功能,但目前尚未实现。


生成change logs

当开始在现有数据库上使用 Liquibase 时,使用生成changelog以创建当前数据库架构通常很有用,尤其是对于测试。Liquibase 允许您使用generateChangeLog command_line 命令执行此操作。

请注意,此命令当前有一些限制。它不导出以下类型的对象:

  • 存储过程,函数和包
  • 触发器
例子
liquibase --driver=oracle.jdbc.OracleDriver \
      --classpath=\path\to\classes:jdbcdriver.jar \
      --changeLogFile=com/example/db.changelog.xml \
      --url="jdbc:oracle:thin:@localhost:1521:XE" \
      --username=scott \
      --password=tiger \
      generateChangeLog
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.1
    http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.1.xsd">
    <changeSet author="diff-generated" id="1185214997195-1">
        <createTable name="BONUS">
            <column name="ENAME" type="VARCHAR2(10,0)"/>
            <column name="JOB" type="VARCHAR2(9,0)"/>
            <column name="SAL" type="NUMBER(22,0)"/>
            <column name="COMM" type="NUMBER(22,0)"/>
        </createTable>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-2">
        <createTable name="DEPT">
            <column name="DEPTNO" type="NUMBER(2,0)"/>
            <column name="DNAME" type="VARCHAR2(14,0)"/>
            <column name="LOC" type="VARCHAR2(13,0)"/>
        </createTable>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-3">
        <createTable name="EMP">
            <column name="EMPNO" type="NUMBER(4,0)"/>
            <column name="ENAME" type="VARCHAR2(10,0)"/>
            <column name="JOB" type="VARCHAR2(9,0)"/>
            <column name="MGR" type="NUMBER(4,0)"/>
            <column name="HIREDATE" type="DATE(7,0)"/>
            <column name="SAL" type="NUMBER(7,2)"/>
            <column name="COMM" type="NUMBER(7,2)"/>
            <column name="DEPTNO" type="NUMBER(2,0)"/>
        </createTable>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-4">
        <createTable name="SALGRADE">
            <column name="GRADE" type="NUMBER(22,0)"/>
            <column name="LOSAL" type="NUMBER(22,0)"/>
            <column name="HISAL" type="NUMBER(22,0)"/>
        </createTable>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-5">
        <addForeignKeyConstraint baseColumnNames="DEPTNO"
            baseTableName="DEPT" constraintName="FK_NAME"
            referencedColumnNames="DEPTNO" referencedTableName="EMP"/>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-6">
        <createIndex indexName="PK_DEPT" tableName="DEPT">
            <column name="DEPTNO"/>
        </createIndex>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-7">
        <createIndex indexName="PK_EMP" tableName="EMP">
            <column name="EMPNO"/>
        </createIndex>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-8">
        <addPrimaryKey columnNames="DEPTNO" tableName="DEPT"/>
    </changeSet>
    <changeSet author="diff-generated" id="1185214997195-9">
        <addPrimaryKey columnNames="EMPNO" tableName="EMP"/>
    </changeSet>
</databaseChangeLog>

数据库文档生成器

在已经存在的数据库中的将更改信息存储到changelog中,Liquibase可以生成数据库更改文档

运行DBDoc

dbDoc命令支持当前仅通过command line可用

样例
liquibase.sh --driver=oracle.jdbc.OracleDriver \
        --url=jdbc:oracle:thin:@testdb:1521:test \
        --username=bob \
        --password=bob \
        --changeLogFile=path/to/changelog.xml
    dbDoc \
        /docs/dbdoc
样本输出

文档输出基于JavaDoc样式文档。此处提供了示例changelog中的更改报告。


更新数据库

Liquibase 允许您应用您和其他开发人员已添加到changelog文件中的数据库更改。

如何跟踪更改集状态

每个changeSet都有一个idauthor属性,该属性与changelog文件的目录和文件名一起唯一标识它。

Liquibase 按顺序读取changelog文件中的changeSet,并将标识符与存储在DatabaseChangeLog表中的值进行比较。如果表中不存在标识符,则运行changeSet,并将新行添加到包含标识符和changeSetMD5Sum哈希的数据库changelog表中。

如果该标识符已存在于databasechangelog表中,则将当前存在的change set的md5sum与数据库中的md5sum进行比较。如果它们不同,Liquibase将抛出一个错误,警告您有人意外更改了它,或者根据runOnChange changeset属性的状态重新执行它。

控制更新

应用未运行changeset有两种模式:

  • update应用于所有的未被运行的更改
  • updateCount仅应用给定数量的未运行更改。
SQL 更新模式

可以将所需的 SQL 存储以供审阅和后续应用程序使用,而不是将changeSet直接应用于数据库。


SQL 输出

根据您的开发和发布过程,您可能不希望Liquibase 直接更新您的数据库。原因可能包括希望调整生成的 SQL、让 DBA批准SQL或实现 SOX 合规性。 因此,所有更新和回滚命令都有sql 输出模式,该模式不针对数据库执行任何操作,而是将生成的 SQL 保存到标准输出或指定文件。


SOX 合规性和数据库重构

管理、跟踪和应用数据库更改是非常困难的,尤其是在敏捷数据库环境中,在项目的整个生命周期中发生了许多更改。即使使用 Liquibase 这样的工具,以一致且可追溯的方式应用数据库更改需要大量训练。

对于需要处理符合SOX版本的项目,该过程更加困难,因为发布文档不仅需要包括如何更新数据库,还需要包括如何在发布出现问题时回滚。

为了解决这一问题,我们增加了对Liquibase的自动回滚支持。对于changelog文件中的每个更改设置,Liquibase可以(通常)生成回滚 SQL。对于无法自动撤消的更改(drop table、插入数据等),或者如果要重写默认回滚方法,可以指定包含正确 SQL 的标签。这种生成回滚命令的方法效果很好,因为在大多数情况下,您不必执行任何操作,并且当您必须指定回滚 SQL 时,它存储在原始更改的一侧。

要控制生成 SQL以更新数据库并回滚数据库的生成,请参阅command line migrator文档。


DATABASECHANGELOG

Liquibase 使用 DATABASEchangeLOG 表来跟踪已运行的changeSet

该表将每个更改设置作为一行进行跟踪,由存储changelog文件的路径的idauthorfilename列的组合标识。

标准数据类型描述
IDVARCHAR(255)changeSet中的id属性值
AUTHORVARCHAR(255)changeSet中的author属性值
FILENAMEVARCHAR(255)changelog的路径。这可能是一个绝对路径或一个相对路径,具体取决于changelog如何传递到 Liquibase。为获得最佳结果,它应该是一个相对路径
DATEEXECUTEDDATETIME执行changeSet的日期/时间。与 ORDEREXECUTED 一起使用以确定回滚顺序
ORDEREXECUTEDINT执行changeSet的顺序。除 DATE EXECUTED外,还用于确保顺序正确,即使数据库日期时间支持较差的精度也是如此。 注: 仅在单个更新运行中保证值增加。有时,它们会在零处重新启动。
EXECTYPEVARCHAR(10)changeSet是如何执行的描述。可能的值有EXECUTED, FAILED, SKIPPED, RERAN, 和 MARK_RAN
MD5SUMVARCHAR(35)执行changeSet时的校验和。用于每次运行,以确保changelog文件中的 changSet 没有意外更改
DESCRIPTIONVARCHAR(255)changeSet生成的可读的描述
COMMENTSVARCHAR(255)changeSetcomment标签的值
TAGVARCHAR(255)changeSet的跟踪对应于标签操作。
LIQUIBASEVARCHAR(20)用于执行changeSetLiquibase 版本
注意事项

该表没有主键。这是为了避免对密钥长度进行特定于数据库的任何限制。


DATABASECHANGELOGLOCK

Liquibase 使用 DATABASEchangeLOGLOG 表确保一次只运行一个 Liquibase 实例。

因为Liquibase 只是从 DATABASEchangeLOG 表读取以确定需要运行的changeSet,因此,如果同时对同一数据库执行多个 Liquibase实例,则会发生冲突。如果多个开发人员使用相同的数据库实例,或者集群中有多个服务器在启动时自动运行 Liquibase,则可能会发生这种情况。

标准数据类型描述
IDINT锁的 ID。目前只有一个锁,但将来是有用的
LOCKEDINT如果 Liquibase正在针对此数据库运行,则设置为"1"。否则设置为"0"
LOCKGRANTEDDATETIME获取锁的日期和时间
LOCKEDBYVARCHAR(255)被谁锁住的描述
注意事项

如果 Liquibase 未干净地退出,则锁住的行可能会保留为锁定状态。您可以通过运行UPDATE DATABASECHANGELOGLOCK SET LOCKED=0清除当前锁


集成JEE CDI

Liquibase可以通过实现多种CDI Procducers 方法在 JEE CDI环境中运行。CDI Liquibase集成是一个简单的 CDI扩展,在 CDI容器启动时执行Liquibase 更新。

如何配置Liquibase
/**
 * A Simple CDI Producer to configure the CDI Liquibase integration
 *
 */
public class LiquibaseProducer {

    @Resource
    private DataSource myDataSource;

    @Produces @LiquibaseType
    public CDILiquibaseConfig createConfig() {
        CDILiquibaseConfig config = new CDILiquibaseConfig();
        config.setChangeLog("liquibase/parser/core/xml/simpleChangeLog.xml");
        return config;
    }

    @Produces @LiquibaseType
    public DataSource createDataSource() throws SQLException {
        return myDataSource;
    }

    @Produces @LiquibaseType
    public ResourceAccessor create() {
        return new ClassLoaderResourceAccessor(getClass().getClassLoader());
    }

}
CDILiquibaseConfig 可用属性
  • changeLog
  • contexts
  • parameters
  • defaultSchema
  • dropFirst 从2.0.2版本开始

如果您不希望 Liquibase 运行,您可以配置以下系统属性liquibase应该run=false


Servlet Listener

Liquibase可以在Servlet Listener中运行。无论何时你的站点被部署,Liquibase都允许你的数据库自动更新。因为Liquibase采用的是分布式锁,即使您有一个应用程序服务器群集,此方法也能正常工作。

为了配置Servlet Listener,需要将liquibase.jar添加到WEB-INF/lib目录下,你的web.xml的内容如下:

<context-param>
    <param-name>liquibase.changelog</param-name>
    <param-value>com/example/db.changelog.xml</param-value>
</context-param>

<context-param>
    <param-name>liquibase.datasource</param-name>
    <param-value>java:comp/env/jdbc/default</param-value>
</context-param>

<context-param>
    <param-name>liquibase.host.includes</param-name>
    <param-value>production1.example.com, production2.example.com</param-value>
</context-param>

<context-param>
    <param-name>liquibase.onerror.fail</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
    <param-name>liquibase.contexts</param-name>
    <param-value>production</param-value>
</context-param>

<listener>
    <listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
</listener>

如果使用的liquibase版本是1.9,web.xml应该添加如下内容:

<context-param>
    <param-name>LIQUIBASE_CHANGELOG</param-name>
    <param-value>com/example/db.changelog.xml</param-value>
</context-param>

<context-param>
    <param-name>LIQUIBASE_DATA_SOURCE</param-name>
    <param-value>java:comp/env/jdbc/default</param-value>
</context-param>

<context-param>
    <param-name>LIQUIBASE_HOST_EXCLUDES</param-name>
    <param-value>production1.example.com, production2.example.com</param-value>
</context-param>

<context-param>
    <param-name>LIQUIBASE_FAIL_ON_ERROR</param-name>
    <param-value>true</param-value>
</context-param>

<context-param>
    <param-name>LIQUIBASE_CONTEXTS</param-name>
    <param-value>production</param-value>
</context-param>

<listener>
    <listener-class>liquibase.servlet.LiquibaseServletListener</listener-class>
</listener>
context-param
参数1.9版本描述
liquibase.changelogLIQUIBASE_CHANGELOG指定要运行的changelog文件。必填
liquibase.datasourceLIQUIBASE_DATA_SOURCE为了运行Liquibase使用JNDI数据源。请注意,如果 LIQUIBASE_DATA_SOURCE没有足够的权限创建/更改表等,则 LIQUIBASE_DATA_SOURCE 可能不同于 Web 应用的其余部分使用的数据源。必填
liquibase.host.excludesLIQUIBASE_HOST_EXCLUDES指定不希望 Liquibase 运行的主机名。指定此参数允许您将相同的 WAR/EAR 部署到不同环境中的多台计算机,并且不会在所有计算机上运行 Liquibase
liquibase.host.includesLIQUIBASE_HOST_INCLUDES指定仅希望 Liquibase 运行的主机名。指定此参数允许您将相同的 WAR/EAR部署到不同环境中的多台计算机,并且不会在所有计算机上运行Liquibase
liquibase.onerror.failLIQUIBASE_FAIL_ON_ERROR指定在发生错误时,Liquibase是否引发异常。将值设置为true(默认值)将导致引发异常,并阻止网站正确初始化。将值设置为false将允许站点正常部署,但数据库将处于未定义状态。
liquibase.contextsLIQUIBASE_CONTEXTS要运行的上下文列表,用逗号分隔开

如果要控制运行 Liquibase 但不想设置 LIQUIBASE_HOST_EXCLUDES/LIQUIBASE_HOST_INCLUDES 属性的服务器,则可以指定liquibase.should.run=[true/false]系统属性。


集成Spring

Liquibase可以通过声明一个liquibase.spring.SpringLiquibasebeanspring的环境中运行。

例子
<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
      <property name="dataSource" ref="myDataSource" />
      <property name="changeLog" value="classpath:db-changelog.xml" />

      <!--
      contexts specifies the runtime contexts to use.
      -->
      <property name="contexts" value="test, production" />
 </bean>
可用的属性
  • beanName
  • changeLog
  • contexts
  • dataSource
  • defaultSchema
  • dropFirst 从版本2.0.2
  • parameters
  • resourceLoader

离线数据库支持

如果您无法直接针对数据库运行 Liquibase,则有两种主要选项可确保数据库保持最新。

updateSQL

我的第一个回答是"你真的需要简化它吗?您构建了很长一段时间的changelog,并且已运行它并测试了无数次。一旦你开始乱搞的changelog文件,你引入的风险有它自己的成本。无论您有什么性能或文件大小问题,您是否真的会超过干扰您知道有效的脚本的风险?

如果值得冒险,为什么它起作用的风险?有时的问题是,您的 changelog 文件变得太大,以至于编辑器会窒息,或者您得到太多的合并冲突。最好的方法是简单地将changelog文件分解为多个文件。创建 master.changelog.xml文件,该文件使用 标记引用其他changelog文件,而不是让单个 changelog.xml文件包含所有内容。

<databaseChangeLog
            xmlns="http://www.liquibase.org/xml/ns/dbchangelog/3.3"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/3.3
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd">
    <include file="com/example/news/news.changelog.xml"/>
    <include file="com/example/directory/directory.changelog.xml"/>
</databaseChangeLog>

当您对 master.changelog.xml 文件运行 liquibase 更新时,将运行 com/example/news/news.changelog.xml中的changesets,然后运行com/example/directory/directory.changelog.xml 中的change sets。您可以以最适合您的任何方式分解changesets。有些按功能分解,有的按发布将其分解。找出最适合你的。

其他时候,问题是liquibase更新需要的时间太长。Liquibase 在将 DATBASECHANGELOG 表的内容与当前changelogs文件进行比较时,尽量提高效率,即使有数千个已运行的changeset,运行update命令也只需几秒钟即可运行。如果您发现更新所花的时间比它应该要长,请查看 Liquibase日志以确定原因。也许是因为设置了runAlways="true",所以运行过的也会运行。changeSet不再需要运行或有不再需要的preConditions。使用 -logLevel=INFO或者-logLevel=DEBUG 运行 Liquibase 可以提供额外的输出,可帮助您确定哪些changeSets速度较慢。一旦您知道什么正在减慢更新速度,请尝试仅更改这些changeSets,而不是放弃整个changeLog并从头开始。您仍希望深入测试changelog,但这是一个风险要小得多的更改。

对于其他人,他们发现 liquibase更新非常适合增量更新,但从头开始创建数据库需要很长时间。我再次问"这真的是个问题吗?是否经常重新创建数据库,以便更改创建脚本的风险有意义?如果是,第一步应该是查找如上所述的问题changeSet。数据库速度很快,尤其是当它们是空的。即使您创建表只是为了再次删除它,通常也只是几毫秒的开销,不值得优化。创建数据库的最大性能瓶颈通常是索引,因此请从索引开始。如果在创建过程中频繁创建和更新索引,则可以将这些changeSet并到更高效的内容中。

当您需要surgically地更改现有changeSets时,请记住 Liquibase 的工作原理:每个change Set 都有一个id、一个author和一个filepath,这些路径共同唯一地标识它。如果 DATABASECHANGELOG 表具有该更改的条目设置,则不会运行它。如果它有一个条目,则如果文件中的 changeSet 的校验和与上次运行时存储的内容不匹配,则它将引发错误。

如何修改现有changeSet还取决于您的环境以及问题changeSetchangelog中的位置。如果要修改已应用于所有环境且现在仅用于新数据库生成上的chaneSet,则可以以不同于已应用于某些数据库但尚未应用于其他数据库的changeSet

要合并或修改现有changeSet,您需要执行编辑现有changeSet、删除旧changeSet和创建新changeSet的组合

删除不需要的changeSet很容易,因为Liquibase 不关心没有相应changeSetDATABASEchangeLOG行。只需删除过期的changeSet,您就完成了。例如,如果您有一个创建表cartchangeSet,而另一个changeSet则删除文件上的两个changeSet。但是,必须确保使用该表的创建和删除之间没有changeSet,否则它们将在新的数据库生成中失败。这是更改changelog文件时引入风险的示例:

相反,假设您有一个cart表,该表是在一个 changeSet 中创建的,然后在另一个更改组中创建promo_code列,在另一个更改中创建abandoned标志。

<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-3.3.xsd">
    <changeSet author="nvoxland" id="1">
        <createTable tableName="cart">
            <column name="id" type="int"/>
        </createTable>
    </changeSet>

    <changeSet author="nvoxland" id="2">
        <addColumn tableName="cart">
            <column name="promo_code" type="varchar(10)"/>
        </addColumn>
    </changeSet>

    <changeSet author="nvoxland" id="3">
        <addColumn tableName="cart">
            <column name="abandoned" type="boolean"/>
        </addColumn>
    </changeSet>
</databaseChangeLog>

一个选项是使用现有id="1" 将所有内容合并到新的changeSet中,然后删除其他changeSet

<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-3.3.xsd">
    <changeSet author="nvoxland" id="1">
        <validCheckSum>7:f24b25ba0fea451728ffbade634f791d</validCheckSum>
        <createTable tableName="cart">
            <column name="id" type="int"/>
            <column name="promo_code" type="varchar(10)"/>
            <column name="abandoned" type="boolean"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

如果所有现有数据库都具有具有已添加的promo_code和已添加的abandon列的cart表,则此方法将正常工作。针对现有数据库运行 Liquibase 只会看到 id="1"已运行,并且不会执行任何新操作。在空白数据库上运行 Liquibase 会创建包含所有列的cart表。请注意,我们必须添加标志或现有数据库将出现错误,指出 id="1"自运行以来已更改。只需使用vaildCheckSum 标签中的错误消息中的校验和,以标记您知道它已更改且新值正常。

如果您有一些尚未添加promo_code和/或abandoned列的数据库,请像以前那样更新原始创建表,但使用 onFail="MARK_RAN"preConditions来处理旧changeSet运行而仍未添加列的情况如果新changeSet运行。

<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-3.3.xsd">
    <changeSet author="nvoxland" id="1">
        <validCheckSum>7:f24b25ba0fea451728ffbade634f791d</validCheckSum>
        <createTable tableName="cart">
            <column name="id" type="int"/>
            <column name="promo_code" type="varchar(10)"/>
            <column name="abandoned" type="boolean"/>
        </createTable>
    </changeSet>

    <changeSet author="nvoxland" id="2">
        <preConditions onFail="MARK_RAN">
            <not><columnExists tableName="cart" columnName="promo_code"/></not>
        </preConditions>
        <addColumn tableName="cart">
            <column name="promo_code" type="varchar(10)"/>
        </addColumn>
    </changeSet>

    <changeSet author="nvoxland" id="3">
        <preConditions onFail="MARK_RAN">
            <not><columnExists tableName="cart" columnName="abandoned"/></not>
        </preConditions>
        <addColumn tableName="cart">
            <column name="abandoned" type="boolean"/>
        </addColumn>
    </changeSet>

</databaseChangeLog>

现在,在现有数据库上已运行所有 3 个changeSetLiquibase 将一如既往地继续运行。对于具有旧cart定义的现有数据库,它将看到 id="2"id="3"的列不存在,然后照常执行。对于空白数据库,它将使用 promo_codeabandoned列创建表,然后在 id="2"id="3"中,它将看到它们已在那里,并标记它们已运行而不重新添加列。但是,警告:使用preConditions会增加更新执行的性能开销,并在updateSQL 模式下被忽略,因为 Liquibase 无法知道在changesets尚未实际执行时它们有多适用。因此,最好尽可能避免使用,但一定要在需要时使用它们。preConditions还会增加changeLog的复杂性,这将需要额外的测试,因此在决定是否修改changeLog逻辑时请记住这一点。有时,最好、最安全的方法是等到所有数据库都拥有列,然后修改changeSet以避免preConditions

cart/promo_code/abandoned示例显示了在修改现有changeSet时可以使用的一些基本模式。类似的patters可用于优化您的瓶颈。请记住,当您更改一个changeSet时,它可能会影响下面的其他changeSets,这些更改可能也需要修改。这很容易失控,所以要注意你在做什么。

如果您最终发现完全重新启动changelog最合算,请参阅"将 Liquibase 引入现有项目",其中描述了如何将 Liquibase添加到现有项目(即使该项目以前由 Liquibase 管理)。


用户和开发人员社区

关于如何使用Liquibase的疑问?

使用liquibase标签可以在StackOverFlow上面找到基本多数关于Liquibase使用的问题http://stackoverflow.com/tags/liquibase.

有新的功能想法?你在Liquibase上面发现了一个BUG?

如果在Liquibase的库中发现了BUG,请在Liquibase Jira.上报告它。

如果是带有扩展的 Bug,请记录它扩展的 github 页面部分. 例如 https://github.com/liquibase/liquibase-hibernate/issues或 https://github.com/liquibase/liquibase-oracle/issues.

所有 Liquibase 管理的扩展都可以在 Liquibase GitHub group上找到

正在寻找Liquibase的最新消息吗?

Liquibase发布公告以及一般项目新闻和文章交叉张贴到Liquibase博客http://www.liquibase.org/blog和邮件列表。

要订阅邮件列表,请使用左侧的"通知"表单。@liquibase

正在找Liquibase的源码吗?

Liquibase 源码挂在 https://github.com/liquibase/liquibase.代码提交应经过标准的 GitHub拉取请求系统。

获取更多的信息,可以参考 开发文档

查看超出stackOverFlow范围的讨论?

需要直接到顶部?

你总是可以联系Nathan Voxland,致力于 Liquibase的开源。 nathan.voxland@liquibase.org 或者@nvoxland

寻求支付支持?

商业培训和支持可从 datical.com上获取。


数据库的"diff"

然而最好跟踪数据库变化的方式是在开发中增加changesets。(可以参考 the problem with database diffs), 有时能够执行数据库差异是有价值的,尤其是在项目即将结束时,作为双重检查,所有必需的更改都包含在changelog中。

运行差异

Diff命令支持通过 command_lineant 两种工具获得。当diff-ing数据库的时候, 像在 Liquibase 中通常那样指定目标数据库(-url-username等标志),并在命令名称后使用附加标志指定基本数据库

例子
liquibase.sh --driver=oracle.jdbc.OracleDriver \
        --url=jdbc:oracle:thin:@testdb:1521:test \
        --username=bob \
        --password=bob \
    diff \
        --referenceUrl=jdbc:oracle:thin:@localhost/XE \
        --referenceUsername=bob \
        --referencePassword=bob
数据库比较

目前,Liquibase 运行以下比较:

  • 版本差异
  • 丢失/不期望的表
  • 丢失/不期望的视图
  • 丢失/不期望的列
  • 丢失/不期望的主键
  • 丢失/不期望的唯一约束
  • 丢失/不期望的外键
  • 丢失/不期望的序列
  • 丢失/不期望的索引
  • 列定义差异(数据类型,自动增长等)
  • 视图定义差异
  • 数据差异 (限制), 默认是 不校验的

(当前)是不校验的:

  • 非外键约束(检查等)
  • 存储过程
  • 数据类型长度

Liquibase 可以区分不同的数据库类型,但由于大小写和数据类型的差异,结果可能会偏差。

控制检查(自 1.8 起)

可以使用diff命令的 diffType 参数控制检查的更改。以下选项可用,可以作为逗号分隔的列表传递:

  • tables [默认]
  • columns[默认]
  • views [默认]
  • primaryKeys [默认]
  • indexes [默认]
  • foreignKeys [默认]
  • sequences [默认]
  • data

如果未指定差异类型,将运行标记为 DEFAULT 的检查。

注意: 这仅仅作用于generateChangeLog命令, 不会作用于diff 或者 diffChangeLog命令。

输出模式

Liquibase 支持两种输出模式:report模式(diff)和changelog模式(diffChangeLog)。在这两种模式下,diff进度在执行过程中都会报告为标准错误。

报告模式

在报告模式下,两个数据库之间的差异描述将报告并标准输出。

Base Database: BOB jdbc:oracle:thin:@testdb:1521:latest
Target Database: BOB jdbc:oracle:thin:@localhost/XE
Product Name: EQUAL
Product Version:
     Base:   'Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
With the Partitioning, OLAP and Data Mining options'
     Target: 'Oracle Database 10g Express Edition Release 10.2.0.1.0'
Missing Tables: NONE
Unexpected Tables: NONE
Missing Views: NONE
Unexpected Views: NONE
Missing Columns:
     CREDIT.MONTH
     CREDIT.COMPANY
     CMS_TEMPLATE.CLASSTYPE
     CONTENTITEM.SORTORDER
Unexpected Columns:
     CATEGORY.SORTORDER
Missing Foreign Keys: NONE
Unexpected Foreign Keys:
     FK_NAME (ID_VC -> STATUS_ID_VC)
Missing Primary Keys: NONE
Unexpected Primary Keys: NONE
Missing Indexes: NONE
Unexpected Indexes: NONE
Missing Sequences: NONE
Unexpected Sequences: NONE
changelog模式

changeLog模式下,将基本数据库升级到目标数据库所需的 XML changeLog发送标准输出。此changeLog可以原样包含,也可以复制到现有changeLog中。如果 diff 命令传递了现有的changelog文件,则新的changeSets将追加到文件末尾。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.1
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.1.xsd">
    <changeSet author="diff-generated" id="1185206820975-1">
        <addColumn tableName="CREDIT">
            <column name="MONTH" type="VARCHAR2(10)"/>
        </addColumn>
    </changeSet>
    <changeSet author="diff-generated" id="1185206820975-2">
        <addColumn tableName="CREDIT">
            <column name="COMPANY" type="NUMBER(22,0)"/>
        </addColumn>
    </changeSet>
    <changeSet author="diff-generated" id="1185206820975-3">
        <addColumn tableName="CMS_TEMPLATE">
            <column name="CLASSTYPE" type="VARCHAR2(255)"/>
        </addColumn>
    </changeSet>
    <changeSet author="diff-generated" id="1185206820975-4">
        <addColumn tableName="CONTENTITEM">
            <column name="SORTORDER" type="NUMBER(22)"/>
        </addColumn>
    </changeSet>
    <changeSet author="diff-generated" id="1185206820975-5">
        <dropColumn columnName="SORTORDER" tableName="CATEGORY"/>
    </changeSet>
    <changeSet author="diff-generated" id="1185206820975-6">
        <dropForeignKeyConstraint baseTableName="CMS_STATUS"
                     constraintName="FK_NAME"/>
    </changeSet>
</databaseChangeLog>

可以使用includeObjectsexcludeObjects参数控制要包括在changelog中的数据库对象。(自 3.3.2 起)

支持的格式为:

  • 对象名称(实际上是 regexp)将匹配其名称与 regexp匹配的任何对象。
  • 类型:与给定类型对象的 regexp名称匹配的名称语法。
  • 如果需要多个表达式,逗号将它们分开
  • 类型:命名逻辑将应用于包含列、索引等的表。

注:名称比较区分大小写。如果需要不敏感逻辑,请使用 "(?i)"正则标志。

过滤例子:

  • table_name只会匹配一个叫table_name的表,不会匹配叫other_table或者 TABLE_NAME的表
  • 可以匹配table_nameTABLE_NAME
  • 在表table_name中的"table_name"会匹配所有的列。
  • table:table_name将会匹配一张叫做table_name的表,而不会匹配一个列叫table_name的表
  • table:table_name,columns:*.lock将会匹配一个叫做table_name的表,并且所有的列都是以lock结尾的。

在 已经存在的项目中添加Liquibase

快速入门指南非常适合启动Liquibase新项目,因为您的changelog文件与您的数据库都是空的,很容易匹配。但是,当您有一个现有项目与现有数据库时,事情会更加复杂。

不幸的是,没有简单的"这是你如何做"的答案,因为有这么多的变化的项目,流程和要求。Liquibase提供了许多工具来帮助该过程,但由您决定针对您特定情况组合它们的最佳方式。

Liquibase 添加到现有项目时,基本上有两种方法:让它看起来像您一直在使用 Liquibase和开始使用 Liquibase

让它看起来像您一直在使用 `Liquibase

The goal of this approach is to have a changelog file that matches the current state of your database. You can run this changeLog against a blank database and the final result will be indistinguishable from your existing databases–as if you used Liquibase from the beginning. This approach is usually the best long term, but it can be more work up front.

此方法的目标是具有与数据库的当前状态相匹配的changelog文件。您可以针对空数据库运行此changelog,最终结果与现有数据库无法区分,就像从一开始就使用 Liquibase 一样。

创建changelog

Creating the changelog to match your database can be done automatically using the generateChangeLog command or be done manually. For any database larger than a few tables, the generateChangeLog command is usually a good idea but make sure you go through the generated changeSets to ensure they are correct. Liquibase does not always detect more complex structures like stored procedures or details like if an index is not clustered. Also, ensure data types are as you expected them.

创建与数据库匹配的changelog可以使用 generateChangeLog 命令自动完成,也可以手动完成。对于任何大于几个表的数据库,生成ChangeLog命令通常是个好主意,但请确保通过生成的changesets,以确保它们是正确的。Liquibase 并不总是检测更复杂的结构,如存储过程或详细信息,例如如果索引未群集。此外,请确保数据类型与预期相同。

Populate the DatabaseChangeLog table

Once you have your changeLog, you need a way to ensure that the pre-Liquibase changeSets are only ran on new, empty databases. The easiest way to do this is generally to use the changeLogSync or changeLogSyncSQL command to execute (or generate) the SQL that marks the starting changeSets as already ran without actually executing them.

As an alternative to the changeLogSync command, you can add contexts on the pre-Liquibase changeSets such as <changeSet ... context="legacy"> and when you run Liquibase on a new database you run with liquibase --contexts=legacy update and on an existing database you run with liquibase --contexts=non-legacy.

Finally, you can add <precondition onFail="MARK_RAN"> tags to the generated changeSets. For example, if you have a <createTable tableName="person"> changeSet, you would add <preconditions onFail="MARK_RAN"><not><tableExists tableName="person"/></not></preconditions> tag. Adding preconditions requires more changes to the changeLog file and introduces a performance penalty because Liquibase must check the database metadata for each changeSet the first run through, this approach is usually best used in isolated cases only.

What is the current state?

Often times a part of the reason to move to Liquibase is because your schemas have diverged over time, so an important question to answer is “If I’m making the changelog file match the current state, what is the current state?” Usually the best answer to that question is “the production database” but it can vary.

How divergent your schemas are will also affect which of the above techniques you use to populate the DatabaseChangeLog table, and it will often times make sense to use multiple approaches. For example, you may want to generate your base changeLogs from the production database and use changeLogSyncSQL to be able to mark them ran on everything from production down. Then you can add your non-released changeSets to the changeLog file with a precondition checking if it has already ran. That will allow Liquibase to automatically figure out the correct state for all your databases from development through production.

We are going to use Liquibase starting……NOW!

Instead of building up a changeLog to match your existing database, you can instead just declare “from now on we are using Liquibase”. The advantage to this is that it much easier to set up because it is just a mandate. Usually this works best going from one version to the next because your databases are all in a reasonably consistent state and you simply start tracking database changes in your next version using Liquibase. Because Liquibase only looks at the DatabaseChangeLog table to determine what needs to run, it doesn’t care what else might be in your database and so it will leave all your existing tables alone and just run the new changeSets.

The biggest disadvantage to this approach is that you cannot bootstrap an empty database with Liquibase alone. A work-around is to take a pre-Liquibase snapshot using your database backup tool and use that as your database seed. Any time you need to create a new database, you first load in the seed and then run Liquibase update.

Depending on how much variation you have between your schemas, even with this approach you may need to rely on preconditions or a “mark changes ran” script in order to standardize and handle those variations.

People and Processes

Finally, remember that starting to use Liquibase–especially on an existing project–isn’t just about how you bootstrap your changeLog file. It is also a question of how you introduce Liquibase into your existing processes and culture.

For many companies and projects, everyone realizes the problems that need fixing and are on board with the advantages of change. For others, however, there can be entrenched interests and strong resistance similar to any other process change. Liquibase provides many tools and approaches that can be used to ease it into an existing process such as SQL output, SQL formatted changelogs, diffChangeLog and more that can be combined in ways that works best for your group.

If you know that introducing Liquibase is going to be complex, either from a technical or processes standpoint, it is usually best to introduce it slowly. Start with it on a new project as a trial run and once you have a good grasp of how it works and available options, apply it to other existing projects.


Supported Databases

Due to variations in data types and SQL syntax, the following databases are currently supported out of the box. Additional databases as well as enhancements to support for the below databases are available through Liquibase extensions

Please find further information about which JDBC driver, URL, classes etc. these databases need, by clicking on the database-specific links in the table below.

DatabaseType NameNotes
MySQLmysqlNo Issues
PostgreSQLpostgresql8.2+ is required to use the “drop all database objects” functionality.
Oracleoracle11g driver is required when using the diff tool on databases running with AL32UTF8 or AL16UTF16
Sql ServermssqlNo Issues
Sybase_EnterprisesybaseASE 12.0+ required. “select into” database option needs to be set. Best driver is JTDS. Sybase does not support transactions for DDL so rollbacks will not work on failures. Foreign keys can not be dropped which can break the rollback or dropAll functionality.
Sybase_AnywhereasanySince 1.9
DB2db2No Issues. Will auto-call REORG when necessary.
Apache_DerbyderbyNo Issues
HSQLhsqldbNo Issues
H2h2No Issues
InformixinformixNo Issues
FirebirdfirebirdNo Issues
SQLitesqliteNo Issues

As of Liquibase v3.1, support for some less common databases has been moved out of Liquibase core and into extensions.

To re-enable support for these databases, install the corresponding extension:

Using Unsupported Databases

Since Liquibase is built on top of standard JDBC, the only ties it has to the underlying database is through the SQL that can vary from DBMS to DBMS. If you attempt to use Liquibase with an unsupported database, it will try to run and will most likely succeed. The only problem you are likely to run into is the current date/time function name. If Liquibase is unable to determine the correct date/time function, you can pass it in via the “command line” and documentation/Ant).

You may also run into problem with the SQL generated by the change/refactoring tags on unsupported databases. The best way to deal with this problem is to first try the standard change/refactoring tags. If it generates an error, you can fall back to the sql change to code whatever change you need to make in a way that your database understands.

If, for some reason, the DatabaseChangeLog table cannot be created on your database, the base creation SQL that you can modify to suit your needs is:

CREATE TABLE DATABASECHANGELOG (id varchar(150) not null,
author varchar(150) not null,
filename varchar(255) not null,
dateExecuted datetime not null,
md5sum varchar(32),
description varchar(255),
comments varchar(255),
tag varchar(255),
liquibase varchar(10),
primary key(id, author, filename))

Reasons for creating the DatabaseChangeLog table yourself include database that require null fields to be specified as such and index limitations that don’t allow primary keys on fields as long. You can change the data types and or data type lengths all you want.

Liquibase Java Doc API

https://www.liquibase.org/javadoc/index.html

Liquibasemaven插件

https://www.liquibase.org/documentation/maven/index.html

感谢网友翠星帮我做校对

在这里插入图片描述

  • 32
    点赞
  • 141
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
当然,我可以为您提供关于Liquibase的教程。Liquibase是一个用于数据库版本控制的开源工具,它可以帮助您管理数据库模式和数据的变更。 以下是Liquibase教程的一些主要步骤: 1. 安装Liquibase:首先,您需要下载并安装Liquibase。您可以从Liquibase官方网站(https://www.liquibase.org/)下载最新的稳定版本。 2. 配置数据库:在开始使用Liquibase之前,您需要配置您的数据库连接。您可以在Liquibase的配置文件(通常是一个XML文件)中指定数据库的连接URL、用户名和密码等信息。 3. 创建数据库变更脚本:Liquibase使用变更脚本来跟踪数据库的变化。您可以通过编写包含DDL(数据定义语言)和DML(数据操作语言)语句的XML、YAML或SQL文件来定义这些变更。每个变更脚本都有一个唯一的标识符,以便Liquibase可以跟踪和应用它们的顺序。 4. 运行Liquibase命令:一旦您创建了数据库变更脚本,您可以使用Liquibase命令行工具来执行这些变更。例如,您可以使用"liquibase update"命令来应用尚未应用的变更。 5. 版本控制:Liquibase允许您对数据库模式和数据进行版本控制。您可以使用"Liquibase update"命令来将数据库更新到特定版本。此外,Liquibase还提供了回滚和验证等功能,以便您可以管理和维护数据库的变化历史记录。 这只是Liquibase教程的一个简单概述,您可以在Liquibase官方文档中找到更详细的信息和示例。祝您使用Liquibase愉快!如果您有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值