Liquibase-数据库版本管理使用

 

 

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在所有数据库上运行以计算新的校验和。
posted @ 2019-02-15 12:02  向南以冬  阅读(6606)  评论(0编辑  收藏  举报