个人感觉得,对Hibernate自主更新数据库,感觉不靠谱,不透明,控制自由度不高,而且有时间很容易就会犯错,比如:用SQL创建VAR型,而在Entity中配置的为CHAR类,那么在运行集合成测试时,自动创建的数据库表中的字符串为CHAR类,而实际SQL脚本期的是VARCHAR类,尽管期的不是试了行为,并在本地bootRun或服务器上运行服务时都会失败。另外,到各测试服务器上手动执行SQL脚本费时费神费力的,干嘛不自动化呢,当然,而且对骂是高级需要DBA手动操作的。最后,写一段自动化程序来自动执行更新,想法是很好的,那如果已经有了一些插件或者库可以帮助你更好地实现这样的功能,为什么不好用了如何充当下瑭,目的的,重新制造轮子是无可厚非的。

其实,以上问题可以通过Flyway工具来解决,Flyway可以实现自动化的数据库版本管理,并能够记录数据库版本网络更新,Flyway databasewhy migration结合示例进行了详细的说明,有兴趣可以参考一下。

Flyway如何工作的?

Flyway对数据库进入本管理主要由Metadata表和6种命令完成,Metadata主要用于记录元数据,每种命令能力和解决的问题包包不一样这些命令进入行述,其中的提示图都来自Flyway的官方文档。

元数据表

Flyway中最核心的就是使用用于记录所有版本演化和状态的元数据表,在Flyway首次启动时会创建默认名为SCHEMA_VERSION的元数据表,其表结构为(以)MySQL

FieldTypeNullKeyDefaultversion_rankint(11)NOMULNULLinstalled_rankint(11)NOMULNULLversionvarchar(50)NOPRINULLdescriptionvarchar(200)NO NULLtypevarchar(20)NO NULLscriptvarchar(1000)NO NULLchecksumint(11)YES NULLinstalled_byvarchar(100)NO NULLinstalled_ontimestampNO CURRENT_TIMESTAMP(NULLinstalled_ontimestampNO execution1TIMESTAMP) yint(1)NOMULNULL

Flyway官网上提供了一个很清晰的演示Flyway是如何工作的,可以参考下。

迁移

Migrate是指把数据库Schema迁移到最新版本,是Flyway工作流的核心功能,Flyway在Migrate时会检查Metadata(元数据)表,如果不存在会创建Metadata表于基本表,Metadata表本变更历史以及Checksum之类的。

Migrate时会扫描指定文件系统或Classpath下的Migrations(可以理解为数据库的版本脚本),并且会逃一比对Metadata表中的已存在的版本日志中,如果有Figration获取这些迁移并按次序Apply到数据库中,否则不需要做任何事情。另外,通常在应用程序启动时应默认执行Migrate操作,从而避免的不支持步骤和。

干净的

Clean相对比较容易理解,即清除掉对数据库Schema中的所有对象,包括表格结构,图像,存储过程,函数以及所有的数据显示等。

Clean操作在开发和测试阶段是非常有用的,它能够帮助快速有效地更新和重新生成数据库表结构,但特别注意的是:不应该在生产中的递归!

信息

Info用于打印所有Migrations的详细信息和状态信息,其实也是通过Metadata表和Migrations完成的,下图很好地提示了Info打印出来的信息。

Info能够帮助快速确定当前的数据库版本,以及查看执行成功和失败的迁移。

证实

Validate是指经验证明已经Apply的Migrations是没有变化的,Flyway是默认是开始经验的。

Validate原理是对元数据表与本地Migrations的校验和值,如果值相同则验证通过,否则验证失败,从而可以防止对已应用到数据库的迁移本改。

基线

Baseline针对已经存在于Schema结构的数据库中的一种解决方案,即现在在非空间数据库中新建元数据表,并把Migrations应用到该数据库中。

Baseline可以应用到特定的版本,这样在已经有表结构的数据库中也可以现实添加Metadata表,并从Flyway进行新的Migrations的管理了。

维修

修复操作能够修复元数据表,该操作在元数据表出现错误时是非常有用的。

修复会修复元数据表的错误,通常有两种用途:

  • 迁移失败的迁移记录,该问题只是针对不支持 DDL 事务的数据库。
  • 重新调整整已经应用的Migratons的校验和值,比如:某个Migratinon已经应用,但本地进行了修改,又期重新应用并调整整要校验和不要负额,操作,否则可能造成其环境失败。

如何使用 Flyway?

这里将主要关注在Gradle和Spring Boot中集并使用Flyway,数据库通常会采用MySQL、PostgreSQL、H2或Hsql等。

正确创建迁移

迁移是指Flyway在更新数据库时是使用的版本脚本,比如:一个基于Sql的迁移命令名为V1__init_tables.sql,内容即是创建所有表的sql语句,另外的Java外,Flyway也支持Migration加载迁移的默认位置为classpath:db/migration,也可以指定
filesystem:/project/folder,其加载是在运行时自动发送归本地执行的。

除了需要指定位置外,Flyway 对 Migrations 的扫描还必须遵循从一个确定的命名模型,Migration 主要分为两类:Versioned 和 Repeatable。

  • 版本化迁移
    通常使用的是Versioned类型,用于版本升级,每个版本都有一个唯一的标志并且只能被应用一次,并且不会再为修改,已经迁移的迁移迁移记录其校验值。其中的版本标准版本号,由一个或多个数字构成,数字之间的分隔符可以取用点或下划线,在运行时下划线其实也是被替换凊换了一颗零会被自动忽略。
  • 可重复迁移
    Repeatable是指可重新加载的Migrations,其每次的更新影响Checksum值,然后都会被重新加载,而不用于版本升级。对管理不稳定的数据库更新库经常有用。可重复的迁移总是在版本之后按顺序执行,但开发者必须自己维护脚本并确保可以重新执行,通常会在sql语句中使用CREATE OR REPLACE来保护证书可重。

默认情况下基于Sql的Migration文件的命令规则如下图所示:

其中的文件名由以下部分组成,除掉使用默认配置外,某些部分还可以自定义规则。

  • prefix: 可配置,前导标志,默认值V表示Versioned,R表示Repeatable
  • version: 标准版本号,由一个或多个数字构成,数字之间的分隔符可用点.或下划线_
  • separator: 可配置,用于分隔版本标签与描述信息,默认为两条下划线__
  • description: 描述信息,文字之间可以用下划线或空格分隔
  • 后缀:可配置,后继标准,默认为.sql

另外,关于如何使用基于Java的迁移,有兴趣可以参考基于Java的迁移。

支持的数据库

当前Flyway支持的数据库还是坚持多的,包括:Oracle, SQL Server, SQL Azure, DB2, DB2 z/OS, MySQL(including Amazon RDS), MariaDB, Google Cloud SQL, PostgreSQL(including Amazon RDS and Heroku), Redshift , Vertica, H2, Hsql, Derby, SQLite, SAP HANA, solidDB, Sybase ASE and Phoenix。
前面来说,个人使用比较多的数据库是PostgreSQL、MySQL、H2和Hsql,针对每种数据库的flyway.url显示示例配置为:

Flyway指令行

Flyway的命令工具支持直接在命令中运行Migrate, Clean, Info, Validate, Baseline and Repair6种命令,不需要借用其他Build工具,不需要应用程序的顺序运行在JVM中运行命令行即可,但需要根据不同的操作系统下载并安装该命令执行工具。Flyway会依次搜索以下配置文件,越靠后的配置会覆盖靠前的配置:

  • /conf/flyway.conf
  • /flyway.conf
  • /flyway.conf

一个典型的Flyway项目目录示例目录结构如下:

更多关于Flyway指令行使用可以参考Flyway命令行。

在Gradle中的应用

首先需要在Gradle中引入Flyway插件,通常有两种方式:

  • 方式一:采用buildscript依赖方式。 123456789 buildscript {repositories {mavenCentral()}dependencies {classpath("org.flywaydb:flyway-gradle-plugin:4.0.3")}}apply plugin: 'org.flywaydb.flyway'
  • 方式二(推荐):采用DSL方式引用插件。 123个插件{id "org.flywaydb.flyway" version "4.0.3"}

而在Gradle中配置Flyway Properties有两种方式:

  • 方式一:在build.gradle中配置Flyway Properties。 12345678910 flyway {url = jdbc:h2:./.tmp/testdbuser = sapassword =}# 或编写:project.ext['flyway.url'] = 'jdbc:h2 :./.tmp/testdb'project.ext['flyway.user'] = 'sa'project.ext['flyway.password'] = ''
  • 方式二:在gradle.properties中配置Flyway Properties。 123 flyway.url = jdbc:h2:./.tmp/testdbflyway.user = saflyway.password =

如果预期在运行 Gradle Clean/Build Tasks 时自动执行 Flyway 的某些任务,可以设置 dependsOn,若不希望隐式执行 Flyway 任务,可以不配置。

 clean.dependsOn flywayRepair # 修复 Flyway 元数据表 build.dependsOn flywayMigrate # 将架构迁移到最新版本

另外,其他任务:flywayInfo、flywayValidate、flywayBaseline分别对应到Flyway的命令。在使用Spring Boot时,运行./gradlew bootRun会自动检查并加载最新的db.migration脚本。

特别注意:在生产环境中不应该执行./gradlew flywayClean,除了你知道自己的行为和目标的,因为应该命令会清除所有的数据库对象,相应的危险。

更多关于Flyway在Gradle中的使用请参考Flyway Gradle Plugin。

与Spring Boot集成

在Spring Boot中,如果加入Flyway的依赖,则会自动引用Flyway并使用默认值,但可以修改并配置FlywayProperties。

 flyway.baseline-description = # 执行基线时标记现有模式的描述。 flyway.baseline-version=1 # 开始迁移的版本。 flyway.baseline-on-migrate=false # 是否对没有元数据表的非空模式执行迁移 flyway.check-location=false # 检查迁移脚本位置是否存在。 flyway.clean-on-validation-error=false # 将清除所有对象。警告!不要在生产中启用! flyway.enabled=true # 启用飞路。 flyway.encoding=UTF-8 # 迁移编码。 flyway.ignore-failed-future-migration=true # 在读取元数据表时忽略未来的迁移。 flyway.init-sqls= # SQL 语句在获取连接后立即执行以初始化连接。 flyway.locations=classpath:db/migration # 迁移脚本的位置。 flyway.out-of-order=false # 允许迁移“乱序”运行。 flyway.placeholder-prefix= # 每个占位符的前缀。 flyway.placeholder-replacement=true # 占位符是否应该被替换。 flyway.placeholder-suffix=} # 每个占位符的后缀。 flyway.placeholders.*= # 在 Sql 迁移中替换的占位符。 flyway.schemas= # 连接和更新的默认模式 flyway.sql-migration-prefix=V # Sql 迁移的文件名前缀 flyway.sql-migration-separator=__ # Sql 迁移的文件名分隔符 flyway.sql- migration-suffix=.sql # Sql migrations的文件名后缀 flyway.table=schema_version # Flyway的元数据表名。 flyway.url = # 要迁移的数据库的 JDBC url。如果未设置,则使用主要配置的数据源。 flyway.user= # 要迁移的数据库的登录用户。如果未设置,则使用 spring.datasource.username 值。 flyway.password = # JDBC 密码,如果你想让 Flyway 创建自己的数据源。 flyway.validate-on-migrate = true # 在类路径中验证 sql 迁移 CRC32 校验和。

若使用 Gradle,通常在 build.gradle 中导入
org.flywaydb:flyway-core:4.0.3依靠后即可使用。可能会有以下几种需求:

  • 在本地Run和Tests都会使用内部存储的数据库,其中的spring.jpa.hibernate.ddl-auto都设置为validate,Schema不需要Hibernate自动生成,并期待使用Flyway,并且真会在线上虚拟环境库,并不期盼使用Flyway,如何实现呢?
    解决方法:可以在common.properties中配置flyway.enabled=false,然后在local或dev的配置中启用Flyway即可。通常推荐使用这种模式,结果却可以对不相同的环境过来,本地Run不会依赖真实数据库,又能保证数据库Schema是脚踏本创建的。
  • 在运行测试会使用内部存在的数据库,只有一个配置文件,不使用Flyway,而在本地bootRun时会使用真实数据库,使用Flyway,毕竟不想每次做冯氏架构手去执脚,如何现实?
    解决方法:设置bootRun.dependsOn动态添加Flyway的依赖即可:123456789 addFlywayDenpendency {doLast {dependencies {compile('org.flywaydb:flyway-core:4.0.3')}}}bootRun.dependsOn=addFlywayDenpendency
  • 若项目目有多个团队同时开发不相同的能力,需要新构建多个分支,并且都会触及到数据库Schema修改,当后期Merge时,Migration的版本不相应如何控制资料库更新的冲击呢?
    解决方法:如果两个分支的数据库更改动有冲突,或者最开始的数据库设计不合理,或者当前数据库修改不合理,那么就需要团囗车队和协调。同时指针对数据库在同一时间段有修改,但不会生成冲突的情况,通常现实项目中主要存在于这种情况,那可以设置flyway.out-of-order=true,这样允许当v1和v3已经被应用后,v2出现时也可以一样。实际在本地使用内存会该该,因为因为该会会自动自动自动自动自动自动,local local local或dev中中中真实真实数据库时时可时可可遇到遇到遇到
    另外,值得一提的是Flyway的参数ignore-failed-future-migration默认认为是true,使用情况形式为:当回滚数据库更改为旧版本时,而metadata表中已经存在了新版会旗帜时,Flyway只会显示警告信息。

结束语

总得来说,Flyway可以有更有效的数据库版本管理方式,如果项目中还未使用,不防试一次。于更轻量级的数据库版本管理工具。如果在使用过程中遇到问题或坑,欢迎留言一起交流讨论。


参考

  • 飞路文档
  • Gradle 插件:Flyway
  • Spring 通用应用程序属性
  • 在启动时执行 Flyway 数据库迁移