# flyway-example **Repository Path**: lzcangqiong/flyway-example ## Basic Information - **Project Name**: flyway-example - **Description**: flyway实战 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 1 - **Created**: 2018-11-28 - **Last Updated**: 2021-05-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flyway-example ### 项目介绍 flyway实战 Flyway是一款开源的数据库版本管理工具,Flyway可以独立于应用实现管理并跟踪数据库的变更。 微服务架构下,随着微服务越来越多,如果采用传统的数据库版本管理方案,工作量巨大,且容易出错。 采用Flyway后,运维人员不再需要关注代码与数据库脚本的配套关系了,并且不需要手动执行升级脚本,无疑将大大减少工作量。 ### flyway介绍 #### 概述 使用Flyway,对数据库的所有更改都称为迁移(migrations)。迁移可以是版本控制的,也可以是可重复的。 版本化迁移有两种形式:常规迁移和撤消迁移。 版本化迁移具有版本、描述和校验和(checksum)。版本必须是唯一的。该描述纯粹是为了让您能够记住每个迁移所做的事情。校验和用于检测意外更改。版本化迁移是最常见的迁移类型。它们被精确地应用一次。 或者,可以通过提供具有相同版本的撤销迁移来撤消它们的效果。 可重复迁移具有描述和校验和,但没有版本。它们不是只运行一次,而是在每次校验和更改时(重新)应用。 在一次迁移运行中,在执行了所有挂起的版本化迁移之后,总是最后应用可重复迁移。可重复迁移按其描述的顺序应用。 默认情况下,版本迁移和可重复迁移都可以用SQL或Java编写,并且可以由多条语句组成。 Flyway自动发现文件系统和Java类路径上的迁移。 为了跟踪哪些迁移已经在何时由谁应用,Flyway在数据库中添加一个schema历史表(flyway_schema_history)。 #### 版本迁移 最常见的迁移类型是**版本迁移**。 每个版本迁移都有一个版本、一个描述和一个校验和。 版本必须是唯一的。 该描述纯粹是为了让您能够记住每个迁移所做的事情。 校验和用于检测意外更改。每个版本的sql是确定的,并且确定后不允许修改,通过校验和(checksum)即可验证sql是否被修改。 而对于可重复执行的迁移SQL,如果检测到校验和与记录的不一致,该SQL会重新执行。 版本化迁移只应用一次;可重复迁移,如果有修改会重新执行。 **版本迁移通常用于**: - 创建/修改/删除 表/索引/外键/... - 更新表中的数据 - 用户数据修正 例如: ~~~ .sql CREATE TABLE car ( id INT NOT NULL PRIMARY KEY, license_plate VARCHAR NOT NULL, color VARCHAR NOT NULL ); ALTER TABLE owner ADD driver_license_id VARCHAR; INSERT INTO brand (name) VALUES ('DeLorean'); ~~~ 每个版本迁移必须分配一个唯一的版本号。 任何用点分隔的数字都是合法的版本号。在大多数情况下,一个简单的递增整数就足够了。 然而Flyway是非常灵活的,下面所有这些版本都是有效的迁移版本: 1 001 5.2 1.2.3.4.5.6.7.8.9 205.68 20130115113556 2013.1.15.11.35.56 2013.01.15.11.35.56 版本化的迁移SQL按其版本的顺序执行,并且只会执行一次。 #### 撤销迁移 撤销迁移与常规版本迁移相反。撤销迁移负责用相同的版本撤销版本迁移的影响。撤消迁移是可选的,不需要运行常规版本迁移。 对于上面的例子,撤销迁移是这样的: DELETE FROM brand WHERE name='DeLorean'; ALTER TABLE owner DROP driver_license_id; DROP TABLE car; ##### 重要提示 虽然撤销迁移的想法很好,但不幸的是,它在实践中有时会失败。 一旦发生了破坏性的更改(删除、删除、截断、…),就会开始遇到麻烦。 即使不这样做,最终也会创建用于恢复备份的自制替代方案,这些方案也需要经过适当的测试。 撤销迁移假定整个迁移成功了,现在应该撤销。这无助于在没有DDL事务的数据库上进行失败的版本迁移。 为什么呢? 迁移在任何一个点都可能失败。如果你的SQL文件中有10个SQL语句,第1个、第5个、第7个或第10个陈述都有可能失败。这根本没有办法提前知道。 相反,写撤销迁移是为了撤销整个版本迁移,在这种情况下不会有帮助。 我们发现另一种更好的方法是在DB和当前部署在生产环境中的代码的所有版本之间保持向后兼容性。这样,迁移失败就不是灾难。 旧版本的应用程序仍然与DB兼容,因此您可以简单地回滚应用程序代码、进行研究并采取纠正措施。 这应该辅之以适当的、经过良好测试的备份和恢复策略。 它独立于数据库结构,并且一旦经过测试并证明可以工作,任何迁移脚本都不能破坏它。 为了获得最佳性能,如果您的基础设施支持这一点,我们建议使用底层存储解决方案的快照技术。 特别是对于较大的数据量,这可能比传统的备份和恢复快几个数量级。 #### 可重复的迁移 可重复迁移具有描述和校验和,但没有版本。 它们不是只运行一次,而是在每次校验和更改时(重新)运行。 这对于管理数据库对象非常有用,这些对象的定义可以简单地在版本控制中的单个文件中维护。 它们通常用于: - (重新)创建视图/过程/函数/包/… - 批量引用数据重新插入 在一次迁移运行中,在执行了所有的版本化迁移之后,总是最后应用可重复迁移。 可重复迁移按其描述的顺序执行。 你需要确保可重复迁移的SQL可以多次执行,而不出现错误。 这通常涉及在DDL语句中使用CREATE或REPLACE子句。 下面是一个可重复迁移的例子: CREATE OR REPLACE VIEW blue_cars AS SELECT id, license_plate FROM cars WHERE color='blue'; ### 基于SQL的迁移 基于SQL的迁移是最常用的迁移方式。这使中方式很直观、容易理解,并且很容易利用现有的SQL脚本工具进行调试。 直接使用SQL脚本实现数据库版本迁移,消除了理解任何中间转换层的需要。 基于SQL的迁移通常用于: - DDL更改(为表、视图、触发器、序列创建/修改/删除语句,…) - 修改表中的数据(表中的CRUD操作) - 批量数据更改 #### 命名规则 Flyway的SQL文件命名必须遵循如下模式: ![flyway SQL迁移命名图](./docs/imgs/flyway_name.png) SQL文件名由以下部分组成: - Prefix(前缀): V 代表版本迁移(可配置),U 代表撤销迁移(可配置), R 代表可重复执行迁移(可配置)。 - Version(版本号): 带有点或下划线的版本号,可以根据您的需要分隔任意多的部分(不适合可重复迁移) - Separator(分隔符): __ (两个下划线) (可配置) - Description(描述): 下划线或空格分隔 - Suffix(后缀): 默认为.sql(可配置) #### 查找SQL文件 Flyway可以从文件系统与Java类路径下查找SQL的迁移脚本。 查找路径默认为“classpath:db/migration”,它是可以配置的(配置项为locations,可以配置多个路径)。 ![flyway SQL迁移命名图](./docs/imgs/find_sql_path.png) 在运行时通过文件系统和Java类路径扫描自动发现新的基于sql的迁移。 一旦您配置了想要使用的位置,Flyway将自动获取任何新的SQL迁移,只要它们符合配置的命名约定。 这种扫描是递归的。 指定目录下的非隐藏目录中的所有迁移也将被获取。 #### 语法 Flyway支持所有常规SQL语法元素,包括: - 单行或多行语句 - 单行注释(-)或多行注释(/**/) - 特定于数据库的SQL语法扩展(PL/SQL, T-SQL,…)通常用于定义存储过程、包、… 此外,对于Oracle, Flyway还支持SQL*Plus命令。 #### 替换占位符 除了常规的SQL语法,Flyway还支持用可配置的前缀和后缀替换占位符。 默认情况下,它寻找ant风格的占位符,比如${myplaceholder}。 这对于抽象环境之间的差异非常有用。 例如: /* 单行注释 */ CREATE TABLE test_user ( name VARCHAR(25) NOT NULL, PRIMARY KEY(name) ); /* 多行 注释 */ -- 占位符(Placeholder) INSERT INTO ${tableName} (name) VALUES ('Mr. T'); ### 基于Java的迁移 基于java的迁移非常适合所有不能使用SQL轻松解决的数据库版本迁移。 比如: - BLOB和CLOB更改 - 高级批量数据更改(重新计算、高级格式更改,…) #### 命名规则 为了能够通让Flyway识别到,基于java的迁移必须实现JavaMigration接口。 然而,通常情况只需要从BaseJavaMigration继承就可以了,因为它采用Flyway的默认命名约定,允许Flyway自动从类名中提取版本和描述。 要做到这一点,类名必须遵循以下命名模式: ![flyway SQL迁移命名图](./docs/imgs/flyway_java.png) 文件名由以下部分组成: - Prefix(前缀):V表示版本迁移,U表示撤消迁移,R表示可重复迁移 - Version(版本):下划线(在运行时自动替换为点)可以分隔任意多的部分(不用于可重复迁移) - Separator(分隔符):默认__(为两个下划线) 当然,你也可以按照自己的规则来命名Java类,这样的话,版本、描述、迁移类别则通过你的类实现的JavaMigration的方法来提供。 #### 查找Java的版本迁移 Flyway在locations属性引用的包中的Java类路径中发现基于Java的迁移。 ![flyway SQL迁移命名图](./docs/imgs/find_java.png) 在运行时通过类路径扫描自动发现新的java迁移。 扫描是递归的。 指定包的子包中的Java迁移也会被获取。 #### 校验和和验证 与SQL迁移不同,Java迁移在默认情况下没有校验和,因此不参与Flyway验证的更改检测。 可以通过实现getChecksum()方法来弥补这一点,然后可以使用该方法提供您自己的校验和,然后将对更改进行存储和验证。 #### Java类示例 package db.migration; import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.Context; import java.sql.PreparedStatement; /** * Example of a Java-based migration. */ public class V1_2__Another_user extends BaseJavaMigration { public void migrate(Context context) throws Exception { PreparedStatement statement = context.getConnection().prepareStatement("INSERT INTO test_user (name) VALUES ('Obelix')"); try { statement.execute(); } finally { statement.close(); } } } #### Spring 如果您的应用程序已经使用了Spring,并且您不想直接使用JDBC,那么您可以轻松地使用Spring JDBC的JdbcTemplate: package db.migration; import org.flywaydb.core.api.migration.BaseJavaMigration; import org.flywaydb.core.api.migration.Context; import java.sql.PreparedStatement; /** * Example of a Java-based migration using Spring JDBC. */ public class V1_2__Another_user extends BaseJavaMigration { public void migrate(Context context) { new JdbcTemplate(new SingleConnectionDataSource(context.getConnection(), true)) .execute("INSERT INTO test_user (name) VALUES ('Obelix')"); } } ### 事务 默认情况下,Flyway总是为每个版本创建一个事务。 你也可以通过修改默认配置 group=true 来设置,所有的迁移在一个事务中执行。 如果Flyway检测到由于数据库的技术限制,某个特定语句无法在事务中运行,那么它就不会在事务中运行该迁移。 相反,它将被标记为非事务性的。 默认情况下,事务和非事务语句不能在迁移运行中混合。 但是,您可以通过将mix属性设置为true来实现这一点。 **重要提示** 如果数据库支持DDL语句事务,失败的迁移将总是回滚(除非它们被标记为非事务性的)。 如果另一方面数据库地支持事务中存在DDL语句(例如mysql中,当事务中遇到DDL语句,会将之前的执行进行隐式提交),因此如果后面出错了,将迁移标记为失败,表明可能需要一些手工清理。 所以尽量做到DDL与DML语句写入到不同的SQL文件中,尽量保证DDL语句的正确性,因为一旦出错需要手动清理。 Schema历史表 为了跟踪各个版本的迁移在何时由谁执行,Flyway向您的数据库Schema中添加了一个特殊的Schema历史表。 您可以将此表视为对针对Schema执行的所有更改的完整审计跟踪。 它还记录了迁移校验和以及迁移是否成功。 ### 迁移状态 - **成功** 迁移成功时,在Flyway的模式历史表中标记为成功。 - **失败回滚** 当迁移失败且数据库支持DDL事务时,将回滚迁移,模式历史表中不会记录任何内容。 - **回滚失败** 当迁移失败且数据库不支持DDL事务时,迁移将在模式历史表中标记为failed,这表明可能需要手动数据库清理。 ### 相关连接 [Flyway官网](https://flywaydb.org "Flyway官网") [Flyway官网文档](https://flywaydb.org/documentation/migrations "官网文档") [Flyway配置说明](https://flywaydb.org/documentation/commandline/migrate#options "Flyway配置项说明") [Flyway的工作原理](https://flywaydb.org/getstarted/how "Flyway的工作原理")