背景:
随着公司应用项目的频繁迭代,或多或少会涉及到数据库表、存储过程和视图等的添加或修改,当多人同一时候开发一个数据库管理和应用系统时,采用合理的方法记录表、存储过程和视图等对象的变动是非常重要的。 之前我们公司采用每次版本迭代,会产生一份新的sql脚本(有可能是在上次的版本中进行迭代进行update,或者是一份全新的完整脚本重新进行数据库表的创建),上传到SVN/GIT;这样的情况导致工作忙碌时,非常多时候开发者忘记记录上传SVN/GIT了,则会引起项目跑不起来,后面追加的脚本也不是很好去管理。新添加团队的人也非常难看清版本号改动的来龙去脉,因此引入数据库版本控制工具还是很必要的。解决方案:
市面上的数据库版本控制工具
flyway(开源),nextep(开源),dbdeploy(开源),Liquibase(开源),SQL Source Control (商业软件)…
鉴于现在使用的主要为Spring Boot项目,同时Spring Boot为两款流行的数据库迁移库提供了自动配置支持。
Flyway(http://flywaydb.org)
Liquibase(http://www.liquibase.org)
所以此文章主要介绍一下springboot集成这两款数据库版本控制工具的一些介绍及原理;
Flyway vs Liquibase:
- Flyway
1)原理:每个应用版本可以生成一个或多个sql脚本文件,Flyway会主动帮你维护一张flyway_schema_history脚本执行的记录表,应用程序下次启动时,Flyway会先看flyway_schema_history里的记录,跳过那些脚本。
2)优点:逻辑简单SQL用起来便捷顺手。
3)缺点:分布式项目同时启动可能会把脚本执行多次,不支持回滚,因为数据库部分语法不一致导致脚本文件不能很好的兼容多个类型的数据库。
4)整合Spring Boot(2.1.12.RELEASE)
首先导入maven依赖(根据boot版本配置的flyway版本为5.2.4)
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
可配置(部分字段笔者也没怎么使用过,谷歌翻译直译过来的,想详细了解可进入官网自行查看)
flyway.baseline-description对执行迁移时基准版本的描述
flyway.baseline-on-migrate当迁移时发现目标schema非空,而且带有没有元数据的表时,是否自动执行基准迁移,默认false.
flyway.baseline-version开始执行基准迁移时对现有的schema的版本打标签,默认值为1.
flyway.check-location检查迁移脚本的位置是否存在,默认true.
flyway.clean-disabled这个属性很重要,它表示是否要清除已有库下的表,如果执行的脚本是 V1__xxx.sql,那么会先清除已有库下的表,然后再执行脚本,而且它默认就是要清除(false),生产环境一定要自己配置设置为 true。
flyway.clean-on-validation-error当发现校验错误时是否自动调用clean,默认false.
flyway.connect-retries尝试连接数据库的最大重试次数,默认0
flyway.enabled是否开启flywary,默认true.
flyway.encoding设置迁移时的编码,默认UTF-8.
flyway.group应用它们时是否在同一事务中将所有未完成的迁移分组在一起, 默认false.
flyway.ignore-future-migrations当读取元数据表时是否忽略将来的迁移,默认true.
flyway.ignore-ignored-migrations当读取元数据表时是否忽略忽略的迁移,默认false.
flyway.ignore-missing-migrations当读取元数据表时是否忽略丢失的迁移,默认false.
flyway.ignore-pending-migrations当读取元数据表时是否忽略挂起的迁移,默认false.
flyway.init-sqls当初始化好连接时要执行的SQL,(可配置多个,逗号分隔)
flyway.installed-by记录在历史记录表中的用户名已应用了迁移。
flyway.locations迁移脚本的位置,默认classpath:db/migration.(可配置多个,逗号分隔)
flyway.mixed是否允许在同一迁移中混合使用事务性和非事务性语句。默认false
flyway.out-of-order是否允许无序的迁移,默认false.
flyway.password目标数据库的密码.
flyway.placeholder-prefix设置每个placeholder的前缀,默认"${".
flyway.placeholder-replacement placeholders是否要被替换,默认true.
flyway.placeholder-suffix设置每个placeholder的后缀,默认}.
flyway.placeholders.[placeholder name]设置placeholder的value
flyway.repeatable-sql-migration-prefix可重复SQL迁移的文件名前缀,默认R。
flyway.schemas设定需要flywary迁移的schema,大小写敏感,默认为连接默认的schema.
flyway.skip-default-callbacks是否跳过默认回调。 如果为true,则仅使用自定义回调。默认false
flyway.skip-default-resolvers是否跳过默认解析器。 如果为true,则仅使用自定义解析器。默认false
flyway.sql-migration-prefix迁移文件的前缀,默认为V.
flyway.sql-migration-separator迁移脚本的文件名分隔符,默认双下划线__
flyway.sql-migration-suffix迁移脚本的后缀,默认为.sql
flyway.tableflyway使用的元数据表名,默认为flyway_schema_history
flyway.target迁移时使用的目标版本,默认为latest version
flyway.url迁移时使用的JDBC URL,如果没有指定的话,将使用配置的主数据源
flyway.user迁移数据库的用户名
flyway.validate-on-migrate迁移时是否校验,默认为true.
2. **Liquibase**
1)原理:先对于flyway支持的脚本类型有sql,xml,yaml json,other(可自定义使用一种或多种)。默认情况下,Bean会在/db/changelog(相对于classpath根目录)里查找db.changelog-master.yaml文件。Liquibase变更集都集中在一个文件里。changeset命令后的那行有一个id属性,要对数据库进行后续变更。可以添加一个新的changeset,只要id不一样就行。此外,id属性也不一定是数字,可以包含任意内容。应用程序启动时,Liquibase会读取db.changelog-master.yaml里的变更集指令集,与之前写入databasechangelog表里的内容做对比,随后执行未运行过的变更集。
2)优点:不仅仅支持sql脚本,还提供了丰富的标签使用xml,yaml及json,编写一份脚本可以在多种 数据库运行DDL语句,支持分布式项目,多个项目启动不会出现并发问题(databasechangeloglock表维护锁),支持回滚
3)缺点:使用其他格式的脚本添加了学习的成本,繁琐的标签及备注对新手不是很友好(这边笔者采用了xml结合sql的方式,使用会比较顺滑,但是牺牲了他对多种数据库的兼容性优势)
4)整合Spring Boot(2.1.12.RELEASE) 首先导入maven依赖(根据boot版本配置的liquibase版本为3.6.3)
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
可配置
liquibase.change-log变更日志配置路径,默认classpath:/db/changelog/db.changelog-master.yaml
liquibase.contexts表示liquibase引用的脚本的上下文,要是项目中多个环境的话【比如,研发,测试,生产等】可以配置这个,否则请略过
liquibase.database-change-log-lock-table用于跟踪并发Liquibase使用情况的表的名称。默认DATABASECHANGELOGLOCK
liquibase.database-change-log-table用于跟踪更改历史记录的表的名称。默认DATABASECHANGELOG
liquibase.default-schema默认的数据库Schema
liquibase.drop-first先删除数据库Schema,默认false
liquibase.enabled开启Liquibase支持,默认true
liquibase.labels使用逗号分隔的运行时标签列表。
liquibase.liquibase-schema用于Liquibase对象的Schema
liquibase.liquibase-tablespace用于Liquibase对象的表空间
liquibase.parameters[parameters name]设置parameters的value日志参数。
liquibase.password待迁移数据库的登录密码
liquibase.rollback-file执行更新时将回滚SQL写入的文件
liquibase.test-rollback-on-update在执行更新之前是否应该测试回滚。默认false
liquibase.url待迁移数据库的JDBC URL。如果没有设置,就使用配置的主数据源。
liquibase.user待迁移数据库的登录用户
使用场景及注意事项:
1.使用场景(笔者个人而言):其实我使用数据库管理根据的初衷一部分是为了在多人开发多版本的情况下去维护一份完整的数据库脚本语句,另外很重要的一部分原因是因为最近在开发一个前置机的项目,需要使用前置机升级其他的应用(A应用升级B应用),为了方便脚本和程序的执行及回滚,我引入了liquibase,主要是他更加满足我的需求;
2.注意事项:
其实这两款工具集成boot项目是非常快速简单的,但是有一些注意事项还是需要提前说明一下
1.如果你开启数据库控制工具之后,每次启动项目默认会找你的数据库脚本执行未执行的脚本,已执行的则跳过(主要由日志表记录,两款工具都有对应的日志表,第一次执行工具会自动帮你创建,你无需关心)
2.每个已经记录在日志表的脚本不允许重新添加修改或删除其内容了,否则抛出异常,你可以删除日志记录让他重新执行一次,但或许部分未修改的内容可能产生double的效果,需要了解(判断文件被修改主要由日志表中的md5字段比较)
部分代码:
pom.xml
application.yml
部分脚本文件
db.changelog-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="db/changelog/db.changelog-init.xml"/>
<include file="db/changelog/db.changelog-1.4.0.xml"/>
<!--<include file="db/changelog/db.changelog-1.5.0.xml"/>-->
</databaseChangeLog>
db.changelog-init.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">
<changeSet id="1.0.0" author="lll">
<tagDatabase tag="version_init"/>
</changeSet>
<changeSet id="1.0.1" author="lll">
<sqlFile path="version_init.sql" relativeToChangelogFile="true"/>
</changeSet>
</databaseChangeLog>
version_init.sql
直接是navicat导出的ddl语句这边只粘贴一个例子(我使用的是postgresql数据库,你们navicat导出来什么样放进去就好了)
CREATE TABLE "public"."test" (
"id" int8 NOT NULL,
"name" int8 NOT NULL,
"create_time" timestamp(6) NOT NULL,
"modify_time" timestamp(6)
);
这边liquibase的脚本可以多种方式定义,官方推荐xml,因个人喜好而定,我贴出的代码仅个人喜好,详细可以查阅:
官网格式规范
另外,回滚部分的话有需要的可以留言,或者去看官方文档