LiquiBase笔记
参考资料:
- Liquibase
- Liquibase的简单使用
- Spring Boot使用Liquibase最佳实践
- liquibase使用
- LiquiBase概述及(spring boot 入门配置)
- Liquibase小知识
简介
LiquiBase是一个用于数据库重构和迁移的开源工具,通过日志文件的形式记录数据库的变更,然后执行日志文件中的修改,将数据库更新或回滚到一致的状态。它的目标是提供一种数据库类型无关的解决方案,通过执行schema类型的文件来达到迁移。
LiquiBase的主要特点有:
- 不依赖于特定的数据库,目前支持包括Oracle/Sql Server/DB2/MySql/Sybase/PostgreSQL/Caché等12种数据库,这样在数据库的部署和升级环节可帮助应用系统支持多数据库。
- 提供数据库比较功能,比较结果保存在XML中,基于该XML你可用Liquibase轻松部署或升级数据库。
- 以XML存储数据库变化,其中以作者和ID唯一标识一个变化(ChangSet),支持数据库变化的合并,因此支持多开发人员同时工作。
- 在数据库中保存数据库修改历史(DatabaseChangeHistory),在数据库升级时自动跳过已应用的变化(ChangSet)。
- 提供变化应用的回滚功能,可按时间、数量或标签(tag)回滚已应用的变化。通过这种方式,开发人员可轻易的还原数据库在任何时间点的状态。
- 可生成数据库修改文档(HTML格式)
- 提供数据重构的独立的IDE和Eclipse插件。
- 日志文件支持多种格式,如XML, YAML, JSON, SQL等;
- 支持多种运行方式,如命令行、Spring集成、Maven插件、Gradle插件等;
为什么使用liquibase?
在日常的项目开发中,开发团队经常将数据库和应用程序视为单独的实体,这样就导致了数据库团队与应用开发团队分离造成团队之间信息交流不畅,信息交流不及时,这种情况通常表现为下面几种行为:
- 手工变更数据库
- 不能与团队的其他成员分享数据库变更
- 使用不一致的方法变更数据库或数据
- 使用低效的手工方法管理数据库版本之间的变更
上面的几种行为都是我们在实际开发中遇到的问题,不仅效率低而且出错的机会也高,就算是老司机也会翻车。
解决上面问题的思路是:减少人工手工变更数据库的机会,用程序管理数据库版本的更替。
使用liquibase 管理数据库的变更
使用步骤
- step1: 创建一个数据库 变更日志(change log)文件。
- step2: 在变更日志文件内部创建一个 变更集(change set)。
- step3: 通过命令行或构建脚本对数据库运行变更集。
- step4: 检验数据库中的变更。
step1:创建change log文件
changelog是LiquiBase用来记录数据库的变更,一般放在CLASSPATH下,然后配置到执行路径中。
changelog支持多种格式,主要有XML/JSON/YAML/SQL,其中XML/JSON/YAML除了具体格式语法不同,节点配置很类似,SQL格式中主要记录SQL语句,这里仅给出XML格式的示例,更多的格式示例请参考文档
changelog.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>
step2: 添加变更集
<?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>
一个标签对应一个变更集,由属性id、name,以及changelog的文件路径唯一标识。changelog在执行的时候并不是按照id的顺序,而是按照changeSet在changelog中出现的顺序。
每个更改集都用“id”属性和“author”属性唯一标识。“作者”属性可以最大限度地减少重复的可能性。
liquibase将每个更改集视为要应用于数据库的原子更改。通常最好只在更改集中包含一个更改。
changelog中的一个changeSet对应一个事务,在changeSet执行完后commit,如果出现错误则rollback。
step3: 运行ChangeSet
liquibase 可以使用命令行 Ant Maven Spring等工具运行。
下面是一个mysql下执行的例子:
liquibase --driver=com.mysql.jdbc.Driver \
--classpath=/path/to/classes \
--changeLogFile=com/example/db.changelog.xml \
--url="jdbc:mysql://localhost/example" \
--username=user \
--password=asdf \
update
step4: 检查你的数据库
你可以看到数据库现在包含一个名为“department”的表。还创建了另外两个表:“databasechangelog”和“databasechangeloglock”。
- databasechangelog表包含已针对数据库运行的所有语句的列表。
- databasechangeloglock表用于确保两台计算机不会同时尝试修改数据库。
后面数据库如果有变动,可以再生成新的changelog文件,再次添加到项目中,达到持续集成的效果。
LiquiBase的spring boot 入门配置
pom.xml
<!-- https://mvnrepository.com/artifact/org.liquibase/liquibase-core -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.5.3</version>
</dependency>
增加 LiquibaseConfig.java
package com.aop8.config;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import liquibase.integration.spring.SpringLiquibase;
@Configuration
public class LiquibaseConfig {
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
//指定changelog的位置,这里使用的一个master文件引用其他文件的方式
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts("development,test,production");
//如果设置为true:第一次执行不会报错,第二次将会报错,导致程序无法启动,所以第一次执行完后一定要改为:false
liquibase.setShouldRun(true);
return liquibase;
}
}
增加 master.xml
路径:src/main/resources 目录下 config/liquibase/master.xml
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="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">
<include file="classpath:config/liquibase/changelog/201712022057_add_entity_Base.xml" relativeToChangelogFile="false"/>
</databaseChangeLog>
<include>
与<includeAll>
标签
<include>
的file属性表示要包含的changelog文件的路径,这个文件可以是LiquiBase支持的任意格式,relativeToChangelogFile如果为true,则表示file属性表示的文件路径是相对于根changelog而不是CLASSPATH的,默认为false。
<includeAll>
指定的是changelog的目录,而不是为文件,如:
<includeAll path="com/example/changelogs/"/>
注意: 目前<include>
没有解决重复引用和循环引用的问题,重复引用还好,LiquiBase在执行的时候可以判断重复,而循环引用会导致无限循环,需要注意!
201712022057_add_entity_Base.xml
路径:
src/main/resources 目录下
config/liquibase/changelog/201712022057_add_entity_Base.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">
<property name="now" value="now()" dbms="mysql"/>
<changeSet id="20171202205" author="WATER">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="tb_member"/>
</not>
</preConditions>
<createTable tableName="tb_member">
<column name="id" type="bigint" autoIncrement="true" remarks="主键" >
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="mobile" type="varchar(50)" remarks="手机号" >
<constraints nullable="false" />
</column>
<column name="real_name" type="varchar(50)" remarks="姓名" >
<constraints nullable="false" />
</column>
<column name="introduce" type="varchar(255)" remarks="说明" >
<constraints nullable="false" />
</column>
<column name="status" type="varchar(10)" remarks="账号状态" />
<column name="created_by" type="varchar(50)" remarks="创建人" >
<constraints nullable="false"/>
</column>
<column name="created_date" type="timestamp" defaultValueDate="${now}" remarks="创建时间" >
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="varchar(50)"/>
<column name="last_modified_date" type="timestamp"/>
</createTable>
</changeSet>
</databaseChangeLog>
LiquiBase 支持很多其他数据库重构,如
添加列
<addColumn tableName="distributor">
<column name="phonenumber" type="varchar(255)"/>
</addColumn>
删除列
<dropColumn tableName="distributor" columnName="phonenumber"/>
使用一个 LiquiBase 变更集插入数据
<changeSet id="3" author="betsey">
<insert tableName="distributor">
<column name="id" valueNumeric="3"/>
<column name="name" value="Manassas Beer Company"/>
</insert>
<insert tableName="distributor">
<column name="id" valueNumeric="4"/>
<column name="name" value="Harrisonburg Beer Distributors"/>
</insert>
</changeSet>
常用命令
changelogSync : 將changelog中未套用至db的change logs标识成已同步
changelogSyncSQL : 同changelogSync,但只產生sql,而不執行同步到db
generateChangeLog : 將目前数据库的结构(默认不包含数据)生成 changeLog 文件
dbDoc : 產生像java doc的文件
diff : 比對兩個数据库間的差異
status : 顯示目前change set有那些change log會被套用到db
rollbackSql:根据回滚版本生成回滚sql
rollback:根据回滚版本生成回滚sql,并在数据库中执行
update : 将changeLog.xml中的数据变动changeset脚本转化为sql语句,直接在数据库中执行
updateSQL : 将changeLog.xml中的数据变动changeset脚本转化为sql语句,并输出到对应的文件中
dropAll : 清空当前数据库,包括liquibase的版本信息
tag : 为当前数据库打上标签
generateChangeLog生成已有数据库的changelog
生成已有数据库的changelog有两种方式,一种是使用数据库工具导出SQL数据,然后changelog文件以SQL格式记录即可;另一种方式就是用generateChangeLog命令,如:``
liquibase --driver=com.mysql.jdbc.Driver \
--classpath=./mysql-connector-java-5.1.29.jar \
--changeLogFile=liquibase/db.changelog.xml \
--url="jdbc:mysql://127.0.0.1:3306/test" \
--username=root \
--password=yourpass \
generateChangeLog
generateChangeLog命令默认只会创建数据库结构的变更日志文件,如果希望创建插入数据的变更日志文件,可以使用参数diffTypes,该参数包括如下可选项:
- tables [DEFAULT]表
- columns [DEFAULT] 列
- views [DEFAULT] 视图
- primaryKeys [DEFAULT] 主键
- indexes [DEFAULT] 索引
- foreignKeys [DEFAULT]
- sequences [DEFAULT]
- data
例如生成数据:
liquibase --driver=com.mysql.jdbc.Driver --classpath=mysql-connector-java-5.1.27.jar --changeLogFile=./dbchangelog.xml --url="jdbc:mysql://localhost:3306/blog" --username=root --password=root --diffTypes=data generateChangeLog
分别生成了两个changeLog文件,一个是inti-table.xml; 一个是init-data.xml。
不过generateChangeLog不支持以下功能:存储过程、函数以及触发器;
生成文件的路径可以设置:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<!--指定数据库连接-->
<propertyFile>src/main/resources/liquibase/liquibase.properties</propertyFile>
<!--指定执行主文件-->
<changeLogFile>src/main/resources/liquibase/master.xml</changeLogFile>
<outputChangeLogFile>src/main/resources/changelog_dev.xml</outputChangeLogFile>
<!-- 是否需要弹出确认框-->
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
<!--输出文件的编码-->
<outputFileEncoding>UTF-8</outputFileEncoding>
<!--执行的时候是否显示详细的参数信息-->
<verbose>true</verbose>
<!--是否每次都重新加载properties-->
<propertyFileWillOverride>true</propertyFileWillOverride>
<rollbackTag>${project.version}</rollbackTag>
<tag>${project.version}</tag>
</configuration>
</plugin>
也可以配置在properties文件中:
changeLogFile=src/main/resources/db/changelog/db.changelog-master.xml
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useSSL=false&jdbcCompliantTruncation=false
username=rootpassword=123456
verbose=true\## 生成文件的路径
outputChangeLogFile=src/main/resources/changelog_dev.xml
rollback
rollback有3中形式,分别是:
- rollbackCount: 表示rollback的changeset的个数;
- rollbackDate:表示rollback到指定的日期;
- rollbackTag:表示rollback到指定的tag,需要使用LiquiBase在具体的时间点打上tag;
liquibase:rollback -Dliquibase.rollbackCount=3
rollbackDate需要注意日期的格式,必须匹配当前平台上执行DateFormat.getDateInstance()得到的格式,比如格式为MMM d, yyyy,示例如:
liquibase:rollback -Dliquibase.rollbackDate="Apr 10, 2016"
rollbackTag使用tag标识,所以需要先打tag,示例如:
liquibase:tag -Dliquibase.tag=tag20160410
然后rollback到tag20160410,如:
liquibase:rollback -Dliquibase.rollbackTag=tag20160410
管理changeLogs
Liquibase基本规范
- ChangeSet id使用[任务ID]-[日期]-[序号],如 T100-20181009-001
- ChangeSet必须填写author
- Liquibase禁止对业务数据进行sql操作
- 使用
<sql>
时,禁止包含schema名称 - Liquibase禁止使用存储过程
- 所有表,列要加remarks进行注释
- 已经执行过的ChangeSet严禁修改。
- 不要随便升级项目liquibase版本,特别是大版本升级。不同版本ChangeSet MD5SUM的算法不一样。
根据发布进行管理
使用Liquibase中提供<include file="xxx"/>
标签,可以将ChangeSet分布在不同文件中。同时<include/>
支持多级引用。
基于此功能可以对项目中的ChangeSet进行有效管理。推荐使用以下规范进行管理。
- 每个发布新建一个文件夹,所有发布相关的ChangeSet文件以及数据初始化文件,均放在些文件夹中。
- 每个发布新建一个master.xml。此master.xml中,include本次发布需要执行的ChangeSet文件
- 根据开发小组独立ChangeSet文件(可选)
- 根据功能独立ChangeSet文件。例如user.xml, company.xml
resources
|-liquibase
|-user
| |- master.xml
| |- release.1.0.0
| | |- release.xml
| | |- user.xml -- 用户相关表ChangeSet
| | |- user.csv -- 用户初始化数据
| | |- company.xml -- 公司相关表ChangeSet
| |- release.1.1.0
| | |- release.xml
| | |- ...