Liquibase 实践指南
1、概述
本文主要介绍了 什么是 Liquibase,以及在 SpringBoot 项目中集成使用 Liquibase 对数据库表进行管理,包括自动创建数据库表、自动初始化数据、更新表结构。
2、认识 Liquibase
官网:Liquibase | Database Refactoring | Liquibase: http://www. liquibase.org/ Liquibase 是用于数据库重构、管理、记录变化与回滚的开源工具。 在写代码的时候,我们使用 Git 或 subversion 对代码进行版本控制,在数据库中,我们可以使用 liquibase 对数据库表进行版本控制。
3、Liquibase 特点
- 支持项目代码多人多分支开发与合并;
- 支持多种数据库类型:MySQL、PostgreSQL、Oracle、Sql Server、DB2、H2等,更多信息请移步官网: http://www. liquibase.org/databases .html ;
- 支持多种变化日志格式:XML、YML、JSON、SQL;
- 支持自定义上下文执行逻辑,可指定需要运行的上下文;
- 集群安全的数据库更新;
- 可生成数据库修改文档(HTML);
- 使用命令对比两个数据库;
- 可以使用 build 工具(Command Line、Ant、Maven)运行,也可以嵌入到 Application 中;
- 可以自动生成数据库 SQL 脚本,供 DBA 重构代码使用;
- 可以使用离线数据库;
4、Liquibase实践
4.1、环境依赖
- windows 操作系统 PC/Mac OS
- Java 1.8 运行环境,liquibase 2.x 依赖 java 1.5+,3.x 依赖 java1.6+
- Maven 3.0
- MySQL 5.7
- liquibase 3.6.3
- (可选)Navicat for MySQL ,用于查看数据库表结构与数据
4.2、使用命令行体验 Liquibase
下载压缩包
官网下载地址: https:// download.liquibase.org/ download/?frm=n liquibase-3.6.3-bin.zip: 我的下载地址
解压文件、下载 JDBC jar 包
将上一步下载好的 liquibase zip 包解压出来。 我电脑上使用的 MySQL ,所以我需要下载 mysql-connector-java 用来连接操作数据库,我这里使用的版本是 8.0.13,下载下来后将这个 jar 包放到 liquibase 根目录下的
lib
文件夹中,使用 liquibase 时,会自动扫描
lib
下的包。
下载地址: mysql-connector-java-8.0.13
创建数据库修改日志文件(database chanagelog file)
liquibase 支持多种格式的日志文件,包括 XML、YML、JSON、SQL,官方推荐使用 xml,个人喜欢更加简洁的 yml 格式。在 liquibase 根目录下创建文件夹
chanagelog
,用来存储修改日志文件。从官网拷贝示例,存储到
chanagelog
目录下,文件名为
master.yml
,文件内容如下:
注意直接从官方文档中考出来的,要修改 databaseChangeLog.preConditions.runningAs.username ,改为自己数据库的用户名,我使用的是 root 。
databaseChangeLog:
- preConditions:
- runningAs:
username: root
- 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)
运行命令
在数据库中新建数据库
test_liquibase
,在 liquibase 根目录打开命令行,执行命令
liquibase.bat --url="jdbc:mysql://localhost:3306/test_liquibase?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8" --changeLogFile="/chanagelog/master.yml" --username=root --password=123456 --driver=com.mysql.cj.jdbc.Driver update
liquibase 命令中必须的必须选项有: –url=数据库连接,其中 serverTimezone=GMT%2B8 参数用来指定时区,在使用高版本 jdbc 时需要,不指定可能会报时区错误 –chanagelog=数据库修改日志文件 –username=数据库用户名 –password=数据库密码 –driver=数据库连接驱动 最后的 update 是 liquibase 命令,必须放到选项的后面。 关于命令的更多内容可以使用liquibase --help
命令查看帮助信息,也可以到官网查询详细信息: http://www. liquibase.org/documenta tion/command_line.html
执行结果:
检查数据库
查看
test_liquibase
数据库,发现多了 4 张表:
打开 databasechanagelog 表,发现在
master.yml
中的 3 个修改都已经被执行了(表中 EXECTYPE 值为 EXECUTED)。
4.3、SpringBoot 项目集成 Liquibase
SpringBoot 提供了自动装配,大大降低了其它组件的使用难度,在使用 Liquibase 时,可以说非常简单了。
4.3.1、创建项目
创建一个 SpringBoot 项目,添加依赖 web、jpa、liquibase,最终项目
pom.xml
如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>xyz.wqf</groupId>
<artifactId>liquibasedemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>liquibasedemo</name>
<description>Demo project for Spring Boot with Liquibase</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--liquibase-->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<!--liquibase-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.3.2、添加 changelog 与数据
为了放管理,我们在项目
resources
目录下创建
liquibase
目录用于存放 liquibase 相关的配置文件,在
liquibase
目录下创建
changelog
目录存放所有 changelog 文件,创建
initdata
目录用于存放初始化项目时的数据。
4.3.2.1、changelog
在
liquibase
目录下的
master.xml
为 liquibase 的入口,通过 include 标签将其它的 changelog 文件引入进来。
- master.xml
```xml <?xml version="1.0" encoding="UTF-8"?>
<include file="classpath:liquibase/changelog/init_00000000000000.xml" relativeToChangelogFile="false"/>
<include file="classpath:liquibase/changelog/user_20190313171720.xml" relativeToChangelogFile="false"/>
```
- init_00000000000000.xml
```xml
<property name="now" value="now()" dbms="mysql,h2"/>
<property name="now" value="current_timestamp" dbms="postgresql"/>
<property name="now" value="sysdate" dbms="oracle"/>
<property name="autoIncrement" value="true" dbms="mysql,h2,postgresql,oracle"/>
<property name="floatType" value="float4" dbms="postgresql, h2"/>
<property name="floatType" value="float" dbms="mysql, oracle"/>
<changeSet id="20190313172000" author="wqf31415">
<createTable tableName="book">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="title" type="varchar(255)" remarks="书名">
<constraints nullable="false"/>
</column>
<column name="description" type="text" remarks="描述">
<constraints nullable="true"/>
</column>
<column name="price" type="${floatType}" remarks="价格">
<constraints nullable="true"/>
</column>
<column name="add_time" type="timestamp" defaultValue="${now}">
<constraints nullable="true"/>
</column>
</createTable>
</changeSet>
```
- user_20190313171720.xml
```xml
<property name="now" value="now()" dbms="mysql,h2"/>
<property name="now" value="current_timestamp" dbms="postgresql"/>
<property name="now" value="sysdate" dbms="oracle"/>
<property name="autoIncrement" value="true" dbms="mysql,h2,postgresql,oracle"/>
<property name="floatType" value="float4" dbms="postgresql, h2"/>
<property name="floatType" value="float" dbms="mysql, oracle"/>
<changeSet id="20190313172800" author="wqf31415">
<createTable tableName="user">
<column name="id" autoIncrement="${autoIncrement}" type="bigint">
<constraints nullable="false" primaryKey="true" />
</column>
<column name="name" type="varchar(255)" remarks="姓名">
<constraints nullable="true"/>
</column>
<column name="password" type="varchar(255)" remarks="密码">
<constraints nullable="true"/>
</column>
<column name="age" type="tinyint" remarks="年龄">
<constraints nullable="true"/>
</column>
<column name="birthday" type="date" remarks="生日">
<constraints nullable="true"/>
</column>
</createTable>
<!--导入数据-->
<loadData tableName="user" file="classpath:liquibase/initdata/user.csv" separator=";" encoding="UTF-8"/>
</changeSet>
```
4.3.2.2、数据
在项目初始化,liquibase 完成建表后,可以导入预先定义好的数据。
- user.csv
id;name;password;age;birthday
1;zhangsan;abc123;18;2001-03-23
2;lisi;ls1990;27;1990-11-08
4.3.3、修改配置文件
修改项目配置文件,添加数据库相关的配置,指定 liquibase 的 changelog文件。
server:
port: 8909
spring:
datasource:
url: jdbc:mysql://172.16.19.229:3306/liquibase_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
show-sql: true
application:
name: LiquibaseDemo
liquibase:
change-log: classpath:liquibase/master.xml # 指定 changelog 文件
4.3.4、运行项目
在运行项目前需要提前创建 url 中指定的数据库,我这里需要创建名为
liquibase_demo
的数据库。 运行项目,查看数据库,建表正确且
user
表中数据正确。
5、SpringBoot 配置文件-属性详解
5.1、SpringBoot 配置项
下表是在 SpringBoot 2.1.3.RELEASE 使用 liquibase 的配置项 在 SpringBoot 1.x 版本中 liquibase 配置项少一些,且配置项没有 spring,如 2.x 中的spring.liquibase.change-log
,在 1.x 中为liquibase.change-log
。
| 配置项 | 默认值 | 注释 | | :---------------------------------------------: | :----------------------------------------------: | :----------------------------------------------------------- | | spring.liquibase.change-log | classpath:/db/changelog/db.changelog-master.yaml | changeLogFile 配置路径 | | spring.liquibase.check-change-log-location | true | 是否检查 changelog 配置路径存在 | | spring.liquibase.contexts | | 只有指定的 context 的 changelog 才会被执行,多个 context 之间以逗号分隔 | | spring.liquibase.default-schema | | 默认数据库 | | spring.liquibase.liquibase-schema | | 用于存储 liquibase 对象的数据库 | | spring.liquibase.liquibase-tablespace | | 用于 liquibase 对象的表空间 | | spring.liquibase.database-change-log-table | DATABASECHANGELOG | 存储数据库改变记录执行情况的表名 | | spring.liquibase.database-change-log-lock-table | DATABASECHANGELOGLOCK | 存储当前使用 liquibase 的用户信息表名 | | spring.liquibase.drop-first | false | 是否先删除表 | | spring.liquibase.enabled | true | 是否启用 liquibase | | spring.liquibase.user | | liquibase 使用的数据库用户名,不指定时使用 spring.datasource 中的 | | spring.liquibase.password | | liquibase 使用的数据库用户密码,不指定时使用 spring.datasource 中的 | | spring.liquibase.url | | liquibase 使用的数据库url,不指定时使用 spring.datasource 中的 | | spring.liquibase.labels | | 指定标签的才会运行,多个标签以逗号分隔 | | spring.liquibase.parameters | | changelog 参数 | | spring.liquibase.rollback-file | | 当执行升级时写回滚 SQL 的文件 | | spring.liquibase.test-rollback-on-update | | 执行更新前是否验证回滚 |
6、Liquibase changelog 标签
6.1、ChangeLog 文件
所有
Liquibase
更改的根目录是
databaseChangeLog
文件。
6.1.1、ChangeLog可用属性
logicalFilePath
:用于在创建
changeSet
的唯一标识符时覆盖文件名和路径。移动或重命名
change logs
时是必需的。
<?xml version="1.0" encoding="utf-8" ?>
<databaseChangeLog logicalFilePath="" 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>
6.1.2、ChangeLog 可用子标签
6.1.2.1、preConditions 前提条件
可以附加
preConditions
到
databaseChangeLog
或
changeSet
,以控制基于数据库状态的更新的执行。
下面是使用
preConditions
的几个原因:
-
记录
changelog
的作者在创建
changelog
时的假设。
-
强制运行
changelog
的用户不违反这些假设。
- 在执行不可恢复的更改(例如dropTable)之前,请执行数据检查。
- 根据数据库的状态来控制运行哪些 changesets 和不运行哪些 changesets 。
可用的属性
-
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
的开头),只有
HALT
和
WARN
两种数值。
AND/OR/NOT
逻辑
可以使用可嵌套
<and>
、
<or>
和
<not>
标签将条件逻辑应用于
preConditions
。如果未指定条件标签,则默认为
<AND>
。
可用的
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参数 。如果还给定了值,则仅当该值与给定值不同时,该值才会失败。
-
property
:要检验的属性的名称, 必填 -
value
:给定属性的必需值。
<customPrecondition>
:
-
可以通过创建实现
liquibase.precondition.CustomPrecondition
接口的类来创建自定义precondition
。自定义类上的参数通过基于<param>
子标签的反射进行设置。参数作为字符串传递到自定义preCondition
。
xml <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
:要将参数设置为的字符串值。 必填
具体属性及使用方法请查看 官方文档
6.1.2.2、property
从Liquibase 1.7开始
Liquibase
允许在
changelog
中动态替换参数。使用
${}
语法描述要替换的参数。
配置参数的值
参数的值会被按照下面的顺序进行查找
-
作为参数传递给您的 Liquibase 运行程序(请参阅
Ant
、command
等文档,了解如何传递它们) ant和command没有了解 -
作为
JVM
系统属性 -
在数据库
ChangeLog
文件本身的参数块(<property>
标签)中 - 作为环境变量
<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>
可用属性
-
name
:表schema
的名称; 必填 -
value
:列表的名称; 必填 -
context
:以逗号分隔列表表示的上下文。 -
dbms
:作为逗号分隔列表给出的数据库类型。 -
global
:定义属性是全局的还是仅限于数据库ChangeLog
的。以true
或false
表示。
<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"/>
6.1.2.3、changeSet
changeset 标签是用于将数据库更改类型分组在一起的标记,并且是Liquibase在数据库上执行的更改单位。在changelog中跟踪由多个changeset 创建的变更列表。
每个
changeSet
标签都由
id
标签、
author
标签和
changelog
的
classpath
名称的组合唯一标签。
id
标签仅用作标识符,它不指示更改运行的顺序,甚至不一定是整数。如果您不知道或不希望保存实际作者,只需使用占位符值,如
UNKNOW
。
当
Liquibase
执行数据库
ChangeLog
时,它按顺序读取
changeSet
,并针对每个
changeSet
检查
databasechangelog
表,以查看是否运行了
id/author/filepath
的组合。如果已运行,则将跳过
changeSet
,除非存在真正的
runAlways
标签。运行
changeSet
中的所有更改后,
Liquibase
将在
databasechangelog
中插入带有
id/author/filepath
的新行以及
changeSet
的
MD5Sum
(见下文)。
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 语句或重构标签 PS:如何进行回滚操作,会在后面专门介绍。
6.1.2.4、include/includeAll
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>
6.2、变更集(changeSet)
变更集,一个
changeSet
中可以有多个修改,每个
changeSet
都需要指定 id 和修改人(author),要求 id + author + filepath 必须唯一,id另外还可以指定上下文(context)、数据库系统(dbms)等信息。 Liquibase changeSet 常用属性(基于 liquibase-3.6)
| 属性名 | 值类型 | 默认值 | 注释 | | :--------------: | :--------------------------: | :----: | :----------------------------------------------------------- | | id | String | | (必需)修改集编号 | | author | String | | (必需)修改人 | | context | String | | 修改上下文,指定后可以修改 springboot 配置文件中
spring.liquibase.context
项,来指定需执行的修改 | | labels | String | | 标签,与 context 功能相同 | | dbms | String | | 数据库系统,只用使用指定的数据库时才会执行,如 mysql、h2、postgresql、oracle等,多个数据库系统时以逗号分隔 | | alwaysRun | Boolean | false | 如果为 true 则每次 update 时都会执行 | | runOnChange | Boolean | false | 如果未 true,则每次 checksum 改变时都会执行 | | ignore | Boolean | false | 是否忽略此修改集 | | failOnError | Boolean | true | 如果为 false ,在执行修改时出现错误,liquibase 不会停止,会继续执行其他修改 | | runInTransaction | Boolean | true | 是否在执行时使用数据库事务管理 | | onValidationFail | 枚举,可选值:HALT、MARK_RAN | HALT | 验证失败后的处理方式,停止运行或记录运行状态 |
示例:
<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"
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 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
<changeSet id="20190314135820" author="wqf31415" context="dev" dbms="mysql"
failOnError="true" onValidationFail="HALT" >
<!-- 修改集内容 -->
</changeSet>
</databaseChangeLog>
6.2.1、创建表(createTable)
用于创建新的数据库表,写在 changeSet 标签内。 createTable 标签属性(基于 Liqubase-3.6)
| 属性名 | 值类型 | 默认值 | 注释 | | :---------: | :----: | :----: | :--------- | | tableName | String | | (必需)表名 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 | | tablespace | String | | 表空间 | | remarks | String | | 注释 |
示例:
<changeSet id="20190314135820" author="wqf31415" context="dev" dbms="mysql"
failOnError="true" onValidationFail="HALT" labels="lab" ignore="false" runOrder="">
<createTable tableName="changelog_example" catalogName="liquibase_demo" schemaName="liquibase_demo"
remarks="创建表changelog_example" tablespace="ts_changelog_example">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="varchar(255)" remarks="姓名" >
<constraints nullable="true" unique="true" uniqueConstraintName="unique_name"/>
</column>
<column name="active" type="bit" defaultValueBoolean="true" defaultValue="0">
<constraints nullable="true" />
</column>
</createTable>
</changeSet>
6.2.1.1、列(column)
在创建表或给表添加列时,需要在 createTable 标签内添加 column 标签,用来指定列的属性。 column 标签常用属性
| 属性名 | 值类型 | 默认值 | 注释 | | :-----------: | :-----: | :----: | :------- | | name | String | | 字段名 | | type | String | | 数据类型 | | defaultValue | String | | 默认值 | | autoIncrement | Boolean | | 自增 | | remarks | String | | 注释 |
6.2.1.2、约束(constraints)
在 column 标签中可以给字段添加约束,如可否为空、主键、唯一等约束条件。 constraints 标签常用属性
| 属性名 | 值类型 | 默认值 | 注释 | | :-------------------: | :-----: | :----: | :--------- | | nullable | Boolean | true | 可否为空 | | notNullConstraintName | String | | 非空约束名 | | primaryKey | Boolean | | 是否主键 | | primaryKeyName | String | | 主键名 | | unique | Boolean | false | 是否唯一 |
6.2.2、添加字段(addColumn)
给已经创建的表添加字段,写在 changeSet 标签内,此标签内用 column 标签定义要添加的字段属性。
| 属性名 | 值类型 | 默认值 | 注释 | | :---------: | :----: | :----: | :--------- | | tableName | String | | (必需)表名 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 |
示例:
<changeSet id="20190314154000" author="wqf" context="dev">
<comment>增加 email 字段</comment>
<addColumn tableName="changelog_example" catalogName="liquibase_demo" schemaName="liquibase_demo">
<column name="email" type="varchar(255)">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
6.2.3、删除字段(dropColumn)
dropColumn 标签用于删除已存在表中的字段,写在 changeSet 标签内,注意这个标签内部不能有内容,否则会报错。
| 属性名 | 值类型 | 默认值 | 注释 | | :---------: | :----: | :----: | :----------- | | tableName | String | | (必需)表名 | | name | String | | 要删除的列名 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 |
示例:
<changeSet id="20190314181622" author="wqf" context="dev">
<comment>删除字段 name</comment>
<dropColumn tableName="changelog_example" columnName="name" schemaName="liquibase_demo" catalogName="liquibase_demo"/>
</changeSet>
6.2.4、插入数据(insert)
insert 标签用于在表中插入数据,写在 changeSet 标签内,需要指定表名等属性,在标签内使用 column 标签指定数据值。
| 属性名 | 值类型 | 默认值 | 注释 | | :---------: | :----: | :----: | :------------------------------------------- | | tableName | String | | (必需)表名 | | dbms | String | | 数据库类型,只有在指定的这种数据库下才会执行 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 |
示例:
<changeSet id="20190314183320" author="wqf">
<comment>插入数据</comment>
<insert tableName="changelog_example" dbms="mysql" catalogName="liquibase_demo" schemaName="liquibase_demo">
<column name="email" value="abc@abc.com"/>
<column name="login_name" value="abc"/>
<column name="password" value="abc123"/>
</insert>
</changeSet>
6.2.5、加载批量数据(loadData)
在 changeSet 中添加 loadData 标签,用来加载数据。
| 属性名 | 值类型 | 默认值 | 注释 | | :---------: | :----: | :----: | :------------------- | | tableName | String | | (必需)表名 | | file | String | | 要加载的数据文件路径 | | separator | String | | 分隔符 | | encoding | String | | 编码格式 | | quotchar | String | " | 引号 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 |
示例:
<changeSet id="20190314183320" author="wqf">
<loadData tableName="user" file="classpath:liquibase/initdata/user.csv" separator=";" encoding="UTF-8"
quotchar=""" schemaName="liquibase_demo" catalogName="liquibase_demo"/>
</changeSet>
6.2.6、修改列名(renameColumn)
在 changeSet 中添加 renameColumn 标签用于修改列名。
| 属性名 | 值类型 | 默认值 | 注释 | | :------------: | :----: | :----: | :--------- | | tableName | String | | (必需)表名 | | oldColumnName | String | | 原列名 | | newColumnName | String | | 新列名 | | columnDataType | String | | 数据类型 | | remarks | String | | 注释 | | catalogName | String | | 目录名 | | schemaName | String | | 数据库名 |
示例:
<changeSet id="20190314184833" author="wqf">
<comment>修改字段名</comment>
<renameColumn tableName="changelog_example" oldColumnName="password" newColumnName="pwd" catalogName="liquibase_demo"
schemaName="liquibase_demo" columnDataType="varchar(255)" remarks="修改字段名"/>
</changeSet>
6.2.7、创建索引(createIndex)
为了提高数据查询速度,我们可以给关键字段添加索引,最好是在建表时就添加索引。如我们给 state 表的
collection_time
、
device_id
字段添加联合索引,配置如下:
<changeSet id="201907100943" author="wqf">
<createIndex tableName="state" indexName="index_collectionTime_deviceId">
<column name="collection_time"></column>
<column name="device_id"></column>
</createIndex>
</changeSet>
6.2.8、添加数据库标签 (tagDatabase)
创建一个数据库标签以便将来回滚。
| 属性名 | 值类型 | 默认值 | 注释 | | ------ | ------ | ------ | -------- | | tag | String | | 标签名称 |
<changeSet author="liquibase-docs" id="tagDatabase-example">
<tagDatabase tag="version_1.3"/>
</changeSet>
6.2.9、Community Change Types
Change Types 是Liquibase对数据库架构所做的变更。
Liquibase Community 版可以进行以下类型的更改:
6.2.9.1、Changes that add something
| Change Types | 释义 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | addAutoIncrement | 将现有的列转换为自动递增(也称为“身份”)列。 | | addColumn | addColumn更改类型将新列添加到现有表。 | | addDefaultValue | 将默认值添加到指定列的数据库定义中。必须设置defaultValue,defaultValueNumeric,defaultValueBoolean或defaultValueDate之一。 | | addForeignKeyConstraint | 将外键约束添加到现有列。 | | addLookupTable | 创建包含存储在列中的值的查找表,并创建新表的外键。 | | addNotNullConstraint | 向现有表添加非空约束。如果传递defaultNullValue属性,则在应用约束之前,列的所有空值都将更新为传递的值。 | | addPrimaryKey | 从现有列或一组列中添加主键。 | | addUniqueConstraint | 向现有列或一组列添加唯一约束。 |
6.2.9.2、Changes that create something
| Change Types | 释义 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | createIndex | 在现有列或一组列上创建索引。 | | createProcedure | 定义存储过程的定义。此更改类型比原始SQL命令更适合用于创建过程,因为它不会尝试删除注释或拆分行。 | | createSequence | 创建一个新的数据库序列。 | | createTable | 创建一个表。 | | createView | 创建一个视图。 |
6.2.9.3、Changes that drop something
| Change Types | 释义 | | ------------------------------------------------------------ | ---------------------- | | dropAllForeignKeyConstraints | 删除全部外键约束 | | dropDefaultValue | 删除默认值 | | dropIndex | 删除索引 | | dropPrimaryKey | 删除主键 | | dropSequence | 删除序列 | | dropUniqueConstraint | 删除唯一约束 | | dropNotNullConstraint | 删除非空约束 | | dropProcedure | 删除存储过程 | | dropColumn | 删除列 | | dropForeignKeyConstraint | 删除指定的一个外键约束 | | dropTable | 删除表 | | dropView | 删除视图 |
6.2.9.4、Changes that rename something
| Change Types | 释义 | | ------------------------------------------------------------ | ------------ | | renameTable | 重命名表名 | | renameColumn | 重命名列名 | | renameView | 重命名视图名 | | renameSequence | 重命名序列名 |
6.2.9.5、SQL Changes
| Change Types | 释义 | | ------------------------------------------------------------ | ------------------------- | | sql | 执行指定 sql | | sqlFile | 执行指定 SQL 文件中的 sql |
6.2.9.6、Other kinds of changes
| Change Types | 释义 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | delete | 从现有表中删除数据。 | | executeCommand | 执行系统命令。由于此更改类型不会像大多数情况下那样生成SQL,因此使用Liquibase命令(例如migrationSQL)可能无法按预期工作。因此,请尽可能使用生成SQL的更改类型。 | | loadData | 将数据添加到Changelog时,将数据从CSV文件加载到现有表中。 | | mergeColumns | 合并两个列中的值(以字符串形式连接),然后将结果值存储在新列中。 | | output | 记录消息并继续执行。 | | setTableRemarks | 为指定数据表添加备注 | | customChange | 自定义 Change | | empty | 空,不执行任何操作 | | insert | 将数据插入到现有表中。 | | loadUpdateData | 将数据从CSV文件加载或更新到现有表中。通过发布检查记录是否存在的SQL批处理,不同于loadData。如果找到,则记录为UPDATE,否则记录为INSERTed。另外,生成DELETE语句以进行回滚。 | | modifyDataType | 修改数据类型 | | setColumnRemarks | 添加列注释 | | stop | 通过消息停止Liquibase执行。主要用于调试和逐步执行变更日志 | | update | 更新现有表中的数据。 | | alterSequence | 更改现有序列的属性。 | | tagDatabase | 创建一个数据库标签以便将来回滚。 |
7、Liquibase & Maven
7.1、在 Maven pom 文件配置 Liquibase
Maven中集成LiquiBase,主要是配置
liquibase-maven-plugin
,首先给出一个示例:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<propertyFileWillOverride>true</propertyFileWillOverride>
<propertyFile>src/main/resources/liquibase/liquibase.properties</propertyFile>
<changeLogFile>${changeLogFile}</changeLogFile>
<driver>${driver}</driver>
<url>${url}</url>
<username>${username}</username>
<password>${password}</password>
</configuration>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>update</goal>
</goals>
</execution>
</executions>
</plugin>
其中
<configuration>
节点中的配置可以放在单独的配置文件里。
changeLogFile:src/main/resources/liquibase/master.xml
driver:com.mysql.cj.jdbc.Driver
url:jdbc:mysql://localhost:3306/test
username:root
password:aries2776
7.2、
liquibase:update
执行changelog中的变更:
mvn liquibase:update
7.3、
liquibase:rollback
rollback有3中形式,分别是:
- rollbackCount: 表示rollback的changeset的个数;
- rollbackDate:表示rollback到指定的日期;
- rollbackTag:表示rollback到指定的tag,需要使用LiquiBase在具体的时间点打上tag;可以在 changeLog 文件中显式定
7.3.1、
rollbackCount
rollbackCount
比较简单,示例如:
mvn liquibase:rollback -Dliquibase.rollbackCount=3
回滚指定数量的 changeSet
7.3.2、
rollbackDate
rollbackDate
需要注意日期的格式,必须匹配当前平台上执行
DateFormat.getDateInstance()
得到的格式,比如我的格式为
MMM d, yyyy
,示例如:
mvn liquibase:rollback -Dliquibase.rollbackDate="Apr 10, 2016"
7.3.3、
rollbackTag
rollbackTag
使用tag标识,所以需要先打tag,示例如:
mvn liquibase:tag -Dliquibase.tag=tag20160410
或者
<?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">
<!-- 创建 tag -->
<changeSet id="8" author="aries">
<tagDatabase tag="20201210-before-tag" />
</changeSet>
<changeSet id="7" author="aries">
<createTable tableName="entity">
<column name="entity_id" autoIncrement="1" type="int">
<constraints primaryKey="true" nullable="false" unique="true"/>
</column>
<column name="name" type="varchar(200)" />
<column name="tenant_api_name" type="varchar(200)" />
<column name="api_name" type="varchar(200)" />
<column name="entity_type" type="varchar(200)" />
<column name="primary_key_field" type="varchar(200)" />
<column name="status" type="int(11)" />
<column name="params" type="json" />
<column name="b_time_field" type="varchar(200)" />
</createTable>
</changeSet>
</databaseChangeLog>
回滚操作:
mvn liquibase:rollback -Dliquibase.rollbackTag=20201210-before-tag
7.4、
liquibase:generateChangeLog
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
<propertyFileWillOverride>true</propertyFileWillOverride>
<!--生成文件的路径-->