- Liquibase-数据库版本管理
- 一、数据库版本管理说明
- 1、liquibase介绍
- 1.1、changelog文件格式
- 2、flyway介绍
- 3、liquibase与flyway比较
- 1、liquibase介绍
- 二、spring boot + liquibase
- 1、gradle配置引入liquibase包:
- 2、修改application.yml或加LiquibaseConfig.java
- 2.1、application.yml
- 2.2、LiquibaseConfig.java
- 3、新增changelog.xml
- 3.1、新增master.xml
- 3.2、新增V1.1__init.sql(以sql语句方式,也可以用xml、yml、json请查看官网)
- 三、gradle + liquibase插件
- 1、修改build.gradle文件
- 1.1、多模块项目在根路径修改build.gradle
- 1.2、子模块修改build.gradle
- 2、命令示例
- 3、命令详解
- 3.1、数据库更新命令
- 3.2、数据库回滚命令
- 3.2.1、直接执行回滚
- 3.2.2、生成回滚脚本
- 3.2.3、生成“未来回滚”脚本
- 3.2.4 回滚命令
- 3.3、差异命令
- 3.4、文档命令
- 3.5、维护命令
- 3.6、必需参数
- 3.7、可选参数
- 3.8、必需的Diff参数
- 3.9、可选的Diff参数
- 3.10、更改日志属性
- 3.11、liquibase.activities详解
- 4、插件升级
- 1、修改build.gradle文件
- 一、数据库版本管理说明
Liquibase-数据库版本管理
一、数据库版本管理说明
数据库迁移工具很多,这里我们选择Flyway和Liquibase来说主要是两个原因,
一是它们都是Java生态圈的,其次就是Spring Boot提供了这两者的内建支持,可以很快应用到产品中。
1、liquibase介绍
LiquiBase是一个用于数据库重构和迁移的开源工具,通过日志文件的形式记录数据库的变更,然后执行日志文件中的修改,将数据库更新或回滚到一致的状态。
LiquiBase的主要特点有:
支持几乎所有主流的数据库,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等 支持多开发者的协作维护 日志文件支持多种格式,如XML, YAML, JSON, SQL等 支持多种运行方式,如命令行、Spring集成、Maven插件、Gradle插件等
1.1、changelog文件格式
changelog是LiquiBase用来记录数据库的变更,一般放在CLASSPATH下,然后配置到执行路径中。
changelog支持多种格式,主要有XML/JSON/YAML/SQL,其中XML/JSON/YAML除了具体格式语法不同,节点配置很类似,SQL格式中主要记录SQL语句,以下示例仅给出SQL格式的示例,更多的格式示例请参考文档
2、flyway介绍
flyway相对简单,直接将你需要执行的SQL语句保存为文件,放入应用中执行即可。
Flyway的好处在于简单,而且直接书写SQL并不需要额外的学习。社区版的不支持UNDO操作,需要购买企业版。
3、liquibase与flyway比较
Flyway 自动升级(自动发现更新项):Flyway 会将任意版本的数据库升级到最新版本。
Flyway 可以脱离JVM 环境通过命令行执行,可以通过Ant 脚本执行,通过Maven 脚本执行(这样就可以在集成环境自动执行),并且可以在应用中执行(比如在应用启动时执行)。 Flyway 规约优于配置:Flyway 有一套默认的规约,所以不需要修改任何配置就可以正常使用。 Flyway 既支持SQL 脚本,又支持Java 代码:可以使用SQL 脚本执行数据库更新,也可以使用Java 代码来进行一些高级数据升级操作。 Flyway 高可靠性:在集群环境下进行数据库升级是安全可靠的。 Flyway 支持清除已存在的库表结构:Flyway 可以清除已存在的库表结构,可以从零开始搭建您的库表结构,并管理您的数据库版本升级工作 Flyway 支持失败修复。新的2.0 版本提供了repair 功能,用于解决数据库更新操作失败问题。 Liquibase 自动升级,将任意版本的数据库升级到最新版本。 Liquibase 可以根据数据库的情况为你生成最后的迁移语句,同时因为数据库变动首先是被Liquibase解析,所以也可以简单支持回滚。 Liquibase 支持大部分常见的数据库变动操作,比如建表,删表,变动字段等等 Liquibase 可以在不使用SQL的情况下造成数据库变动,其可读性更高一些,特别是团队并不直接使用SQL而整体相关知识储备不完善的情况下优势更明显。 两款数据库迁移工具其实定位上是差别的。一般小项目整体变动不大的用Flyway,大应用和企业应用用Liquibase更合适。
二、spring boot + liquibase
注:spring boot + liquibase此方式仅仅是简单的实现更新数据库表结构,用于启动项目时更新表结构。
1、gradle配置引入liquibase包:
dependencies {
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3' testCompile group: 'junit', name: 'junit', version: '4.12' }
2、修改application.yml或加LiquibaseConfig.java
2.1、application.yml
liquibase:
change-log: classpath:/db/changelog/master.xml
user: root
password: 1qaz2wsx url: jdbc:mysql://127.0.0.1:3306/test_1.0.1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullNamePatternMatchesAll=true&useSSL=true drop-first: false
2.2、LiquibaseConfig.java
@Configuration
public class LiquibaseConfig { @Bean public SpringLiquibase liquibase(DataSource dataSource) { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setDataSource(dataSource); liquibase.setChangeLog("classpath:/db/changelog/master.xml"); liquibase.setContexts("development,test,production"); liquibase.setShouldRun(true); return liquibase; } }
3、新增changelog.xml
3.1、新增master.xml
在src/main.resouces下新增db/changelog/master.xml。
master.xml内容如下:
<?xml version="1.0" encoding="utf-8"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> <include file="classpath:/db/changelog/V1.1__init.sql" relativeToChangelogFile="false"/> </databaseChangeLog>
3.2、新增V1.1__init.sql(以sql语句方式,也可以用xml、yml、json请查看官网)
在src/main.resouces/db/changelog下新增db/changelog/V1.1__init.sql。
V1.1__init.sql内容如下:
--liquibase formatted sql
--changeset whx:1.1
SET FOREIGN_KEY_CHECKS=0;
-- ---------------------------- -- Table structure for test_user_tab -- ---------------------------- DROP TABLE IF EXISTS `test_user_tab`; CREATE TABLE `test_user_tab` ( `userId` int(11) NOT NULL AUTO_INCREMENT, `userAccount` varchar(16) NOT NULL, `password` varchar(32) NOT NULL, `userStatus` tinyint(1) NOT NULL DEFAULT '1', `addTime` datetime NOT NULL, PRIMARY KEY (`userId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='测试用户表'; --changeset whx:1.2 ALTER TABLE `test_user_tab` DROP COLUMN `addTime`; --rollback ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --changeset whx:1.3 ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`; --changeset whx:1.4 ALTER TABLE `test_user_tab` DROP COLUMN `addTime`; --rollback ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --changeset whx:1.5 ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`;
启动项目可以查看数据库此时新增了三张表:
databasechangelog
databasechangeloglock
test_user_tab
三、gradle + liquibase插件
plugin地址:https://github.com/liquibase/liquibase-gradle-plugin
1、修改build.gradle文件
1.1、多模块项目在根路径修改build.gradle
注:此方式将使所有model都有liquibase插件,在执行命令可能因为文件重复导致失败,不建议此方式。
group = 'test'
version = '0.0.1-SNAPSHOT'
buildscript {
ext {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.org.liquibase:liquibase-gradle-plugin:2.0.1" } } subprojects { apply plugin: 'java' apply plugin: 'eclipse' apply plugin: "org.liquibase.gradle" sourceCompatibility = 1.8 repositories { mavenCentral() maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } } dependencies { liquibaseRuntime 'org.liquibase:liquibase-core:3.5.3' liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl:2.0.1' liquibaseRuntime 'mysql:mysql-connector-java:5.1.34' } liquibase { activities { main { changeLogFile "${this.rootDir}/user/src/main/resources/db/changelog/V1.1__init.sql" url "jdbc:mysql://127.0.0.1:3306/test_1.0.1?useUnicode=true&characterEncoding=UTF-8" username "root" password "1qaz2wsx" } runList = 'main' } } }
1.2、子模块修改build.gradle
例如:core model的core.gradle文件,推荐此方式。
group 'projectManage'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8 buildscript { repositories { mavenCentral() maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath "org.liquibase:liquibase-gradle-plugin:2.0.1" } } apply plugin: 'org.liquibase.gradle' dependencies { liquibaseRuntime 'org.liquibase:liquibase-core:3.5.3' liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl:2.0.1' liquibaseRuntime 'mysql:mysql-connector-java:5.1.34' compile project(":common") testCompile group: 'junit', name: 'junit', version: '4.12' liquibase { activities { main { changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/V1.1__init.sql" //changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/changelog.mysql.sql" url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8" username "root" password "1qaz2wsx" } runList = 'main' } } }
说明:
0、注意mysql驱动版本
liquibaseRuntime 'mysql:mysql-connector-java:5.1.34'
1、注意${this.rootDir}
changeLogFile "${this.rootDir}/user/src/main/resources/db/changelog/V1.1__init.sql"
如果不加${this.rootDir}可能会报Gradle Liquibase change log file could not be found
另一种写法:
liquibase {
activities {
main {
changeLogFile 'src/main/resources/db/dbchangelog-master.xml' url 'jdbc:mysql://localhost:3306/test' username 'XXX' password 'XXX' classpath "$rootDir" } } runList = 'main' }
2、命令示例
0、插件命令 gradle command -PliquibaseCommandValue=<value> # -PliquibaseCommandValue=<value> 为非填。 1、gradle update -PrunList=main #执行命令,将按V1.1__init.sql中的sql语句更新数据库。 2、gradle generateChangeLog #数据库sql文件的标准输出,执行于已存在表结构的数据库将会得到表数据结构文件。 注:changeLogFile 'src/main/resources/db/changelog.mysql.sql' ,changelog.xxx.sql允许不存在,执行时将自动创建。xxx代表的是数据库。 例如:changelog.h2.sql,必须是此格式,否则将报错。 3、gradle rollbackCount -PliquibaseCommandValue=1 # rollbackCount 回滚最后的<value>更改集 4、gradle dbDoc # 将在/项目路径/.idea/dataSources下生成/xxx.xml文件,该文件是对数据库表数据结构的描述。
3、命令详解
liquibase官网:https://www.liquibase.org/documentation/command_line.html
注:插件命令 gradle command -PliquibaseCommandValue=<value>
以下操作均已/core/src/main/resources/db/changelog/V1.1__init.sql中的V1.1__init.sql为例子。
3.1、数据库更新命令
命令 | 描述 |
---|---|
update | 更新数据库到当前版本。 |
updateCount <value> | 更新数据库到指定的value版本,即第几个changeset。 例如:gradle updateCount -PliquibaseCommandValue=1 将只会执行V1.1__init.sql中--changeset whx:1.1,后续的changeset将不会执行。 若继续执行value=5,则--changeset whx:1.1~1.5都将执行,且之前的操作并不会冲突。 |
updateSQL | 预执行sql(执行全部changeset),并不会更新数据库,仅在控制台输出。 |
updateCountSQL <value> | 预执行sql到指定的value版本。 |
3.2、数据库回滚命令
Liquibase有三种管理回滚的模式:
3.2.1、直接执行回滚
可以直接针对目标数据库执行回滚命令。如果无法回滚任何更改,您将收到通知,并且不会回滚任何更改。
3.2.2、生成回滚脚本
可以生成回滚数据库所需的SQL,而不是实际更新数据库。如果要预览在实际运行之前将执行的回滚命令,这将非常有用。
3.2.3、生成“未来回滚”脚本
此模式旨在允许在生成迁移脚本的同时生成回滚脚本。它允许获取更新的应用程序并生成SQL以将数据库更新为新版本以及SQL,以便在需要时将该新版本恢复到当前版本。当DBA想要控制进入数据库的SQL时,以及需要内部和/或“SOX兼容”进程的回滚文档的应用程序时,此功能非常有用。无需在此模式下指定回滚日期,标记或计数。
3.2.4 回滚命令
命令 | 描述 |
---|---|
rollback <tag> | 要回滚到那个tag。例如:gradle rollback -PliquibaseCommandValue=tag20190107。 需要注意的是在V1.1__init.sql中,必须有--rollback。 例如: --changeset whx:1.5 ALTER TABLE `test_user_tab` ADD COLUMN `addTime` datetime NOT NULL AFTER `userStatus`; --rollback ALTER TABLE `test_user_tab` DROP COLUMN `addTime`; 初始版本的sql数据表结构可以没有--rollback,但是后续的--changeset建议都加上--rollback。 |
rollbackToDate <date/time> | 设置回滚的日期。日期格式要符合插件执行theDateFormat.getDateInstance()操作设置的日期格式。 |
rollbackCount <value> | value指定往前回滚几个版本,例如:gradle rollbackCount -PliquibaseCommandValue=1 将会回滚--changeset whx:1.5的操作。 |
rollbackSQL <tag> | 根据tag,预执行rollback。并不会更新数据库,仅在控制台输出。 |
rollbackToDateSQL <date/time> | 根据date/time,预执行rollback。 |
rollbackCountSQL <value> | 根据value,预执行rollback。 |
futureRollbackSQL | SQL以在应用更改日志中的更改后将数据库回滚到当前状态。 |
updateTestingRollback | 更新数据库,然后在更新之前回滚更改。 |
generateChangeLog | 数据库sql文件的标准输出,执行于已存在表结构的数据库将会得到表数据结构文件。 注:changeLogFile 'src/main/resources/db/changelog.mysql.sql', changelog.xxx.sql允许不存在,执行时将自动创建。xxx代表的是那种数据库。 例如:changelog.h2.sql,必须是此格式,否则将报错。 |
3.3、差异命令
diff命令用于比较数据库之间的异同。
注:它目前不检查:非外键约束(检查等)、存储过程、数据类型长度。
详见 3.11、liquibase.activities详解。
命令 | 描述 |
---|---|
diff [diff parameters] | 将差异描述写入标准输出。 例如:gradle diff -PrunList=diffMain。 |
diffChangeLog [diff parameters] | 写入更改日志XML以将基础数据库更新到目标数据库以标准输出,默认控制台输出。 例如:gradle diffChangeLog -PrunList=diffMain。 配置changeLogFile 则按规则输出。 |
3.4、文档命令
命令 | 描述 |
---|---|
dbDoc <outputDirectory> | 默认将在/项目路径/.idea/dataSources下生成/xxx.xml文件,该文件是对数据库表数据结构的描述。 outputDirectory指定输出路径,不同后缀文件名将生产不同格式的Doc。 例如 gradle dbDoc -PliquibaseCommandValue= D:\work_idea\yboa\pro_manage_dev\user\src\main\resources\db\changelog\123.sql 将生成html文件。 |
3.5、维护命令
命令 | 描述 |
---|---|
tag <tag> | "标记"当前数据库状态以供将来回滚。 例如:gradle tag -PliquibaseCommandValue=tag20190107 |
tagExists <tag> | 检查给定标记是否已存在。 |
status | |
validate | 检查更改日志中的错误。 |
changelogSync | 将所有更改标记为在数据库中执行。 |
changelogSyncSQL | SQL以将在数据库中执行的所有更改标记为STDOUT。 |
markNextChangeSetRan | 将下一个更改集标记为在数据库中执行。 |
listLocks | 列出当前锁定数据库更改日志的人员。 |
releaseLocks | 释放数据库更改日志上的所有锁定。 |
dropAll | 删除用户拥有的所有数据库对象。请注意,不删除函数,过程和包(1.8.1中的限制)。 |
clearCheckSums | 从数据库中删除当前的校验和。在下次运行时,将重新计算校验和。 |
3.6、必需参数
详见:build.gradle文件中的配置
命令 | 描述 |
---|---|
--changeLogFile=<path and filename> | 要使用的changelog文件。 |
--username=<value> | 数据库用户名 |
--password=<value> | 数据库密码。 |
--url=<value> | 数据库JDBC URL。 |
--driver=<jdbc.driver.ClassName> | 数据库驱动程序类名。 |
3.7、可选参数
命令 | 描述 |
---|---|
--classpath=<value> | 包含迁移文件和JDBC驱动程序的类路径。 |
--contexts=<value> | ChangeSet上下文要执行。 |
--defaultSchemaName=<schema> | 指定用于托管数据库对象和Liquibase控制表的默认架构。 |
--databaseClass=<custom.DatabaseImpl> | 指定要使用的自定义数据库实现 |
--defaultsFile=</path/to/file> | 包含默认选项值的文件。(默认值:./ liquibase.properties) |
--includeSystemClasspath=<true or false> | 在Liquibase类路径中包含系统类路径。(默认值:true) |
--promptForNonLocalDatabase=<true or false> | 提示非本地主机数据库。(默认值:false) |
--currentDateTimeFunction=<value> | 覆盖SQL中使用的当前日期时间函数。适用于不受支持的数据库。 |
--logLevel=<level> | 执行日志级别((debug, info, warning, severe, off)。 |
--help | 输出命令行参数帮助。 |
--exportDataDir | 将保留insert语句csv文件的目录(generateChangeLog命令所需)。 |
--propertyProviderClass=<properties.ClassName> | 要使用的自定义Properties实现 |
3.8、必需的Diff参数
命令 | 描述 |
---|---|
--referenceUsername=<value> | 基础数据库用户名 |
--referencePassword=<value> | 基础数据库密码。 |
--referenceUrl=<value> | 基础数据库URL。 |
3.9、可选的Diff参数
命令 | 描述 |
---|---|
--referenceDriver=<jdbc.driver.ClassName> | 基础数据库驱动程序类名。 |
3.10、更改日志属性
命令 | 描述 |
---|---|
-D<property.name>=<property.value> | 传递名称/值对以替换更改日志中的$ {}块。 |
3.11、liquibase.activities详解
liquibase {
activities {
main {
changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/V1.1__init.sql"
//changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/changelog.mysql.sql"
url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8" username "root" password "1qaz2wsx" //driver "" // 该参数可非必填,url将自动匹配驱动。 //exportDataDir // 将保留insert语句csv文件的目录(generateChangeLog命令所需)。 } security { changeLogFile 'src/main/db/security.groovy' url project.ext.securityUrl username project.ext.securityUsername password project.ext.securityPassword } /**比较数据库之间的差异**/ diffMain { //若不配置changeLogFile 则将在控制台进行xml输出。diff.mysql.sql会自动创建,必须以 *.databaseType.sql才会生成sql文件 changeLogFile "${this.rootDir}/core/src/main/resources/db/changelog/diff.mysql.sql" url "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.1?useUnicode=true&characterEncoding=UTF-8" username "root" password '1qaz2wsx' referenceUrl "jdbc:mysql://127.0.0.1:3306/yb_oa_xmgl_1.0.2?useUnicode=true&characterEncoding=UTF-8" referenceUsername "root" referencePassword '1qaz2wsx' } runList = 'main' # 不同环境可执行不同的main方法, 例如:runList = 'diffMain' } }
4、插件升级
升级Liquibase Gradle插件本身的版本
大多数时候,Liquibase的新版本与旧版本的版本相同,但有时新版本与现有的更改集存在兼容性问题,就像Liquibase 3发布时一样。
发生这种情况时,建议执行以下升级程序:
1、确保所有Liquibase的管理数据库是最新通过运行 gradle update它们升级到Liquibase插件的新版本之前。 2、创建一个新的丢弃数据库来测试Liquibase更改集。gradle update使用最新版本的Liquibase插件在新数据库上运行 。 这很重要,因为Groovy DSL中的项目已弃用,并且因为不同Liquibase版本生成SQL的方式存在一些细微差别。 例如,使用defaultValue: "0"Liquibase 2中的工作正常,在MySql中向布尔列添加默认值,但在Liquibase 3中, 它生成的SQL不适用于MySql - defaultValueNumeric: 0需要使用它。 3、一旦确定所有更改集都使用最新的Liquibase插件,请清除所有由Liquibase 2旧版本计算的校验和,方法是gradle clearChecksums对所有数据库运行。 4、最后,gradle changeLogSync在所有数据库上运行以计算新的校验和。