前言
从新开发软件到版本迭代可能会遇到的问题
- 新版本开发过程中,开发人员的数据库变更如何更准确地反应到提交的文件中。(比如,A 在开发过程中添加了一个忘记写变动脚本的字段)
- 某些预设的系统配置,没有设置的时候可能会出现错误。
- 版本迭代的时候,老版本的数据库结构和数据的处理。
一、flyway
Flayway是一款数据库版本控制管理工具,,支持数据库版本自动升级,Migrations可以写成sql脚本,也可以写在java代码里;不仅支持Command Line和java api ,也支持Build构建工具和Spring boot,也可以在分布式环境下能够安全可靠安全地升级数据库,同时也支持失败恢复。
快速开始:
1、加入jar包
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.0.7</version>
</dependency>
<!-- 添加插件: -->
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
</plugin>
2、加入配置
spring.datasource.url=jdbc:mysql://xxxxxx:3306/liquibase_db?useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=xxx
spring.datasource.password=xxx
3、加入执行sql (例如:V1.0.0_20191026_01__addTable.sql)
4、启动项目
5、特殊说明
1)配置不开启sql脚本管理
spring.flyway.enabled=false
2)Flyway加载Migrations的默认Locationsclasspath:db/migration可以配置更改。
spring.flyway.locations=classpath:db
3)默认生成了flyway-schema-history表,这个版本默认是这个表,如果想自己指定schema表的命,可以设置:
spring.flyway.table=schema_version_user
4)命名规范 例如:V1.0.0_20191026_01__addTable.sql
- prefix: 可配置,前缀标识,默认值V表示Versioned,R表示Repeatable
- version: 标识版本号,由一个或多个数字构成,数字之间的分隔符可用点.或下划线_
- separator: 可配置,用于分隔版本标识与描述信息,默认为两个下划线__
- description: 描述信息,文字之间可以用下划线或空格分隔
- suffix: 可配置,后续标识,默认为.sql
5)sql脚本尽可能可重复执行
6)对已经存在表结构数据的已有数据库使用flyway需要注意的几个地方。
a、直接使用的话,启动时会报如下错误:
Caused by: org.flywaydb.core.api.FlywayException: Found non-empty schema(s) `test` without schema history table! Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
b、解决方案:设置setBaselineOnMigrate为true。
spring.flyway.baseline-on-migrate=true
c、但此时如果有脚本需要启动加载执行的话,脚本的版本号一定要比1大才能执行(注意红色标注),举个例子V1.0__Base_version.sql不会被执行,但V1.1__Base_version.sql就会被执行。
7)若执行sql脚本失败,flyway会记录失败信息到flyway_schema_history表,并且下次重新启动flyway前必须先删掉失败信息,否则会报错
8)若sql已经执行,建议不要在其上面再次需改(空格,换行可以使用),可以新建一个文件更新,必须要更改时,可以删除数据库记录,即可。(因为启动时,默认是有文件记录校验,这个配置也可以配置去除 , flyway.validate-on-migrate=false # 在运行迁移时是否要自动验证。 默认值: true)
9) 附录: Flyway配置详解
flyway.baseline-description= # 执行基线时标记已有Schema的描述
flyway.baseline-version=1 # 基线版本默认开始序号 默认为 1.
flyway.baseline-on-migrate=false # 针对非空数据库是否默认调用基线版本 , 这也是我们上面版本号从 2 开始的原因
flyway.check-location=false # 是否开启脚本检查 检查脚本是否存在 默认false
flyway.clean-on-validation-error=false # 验证错误时 是否自动清除数据库 高危操作!!!
flyway.enabled=true # 是否启用 flyway.
flyway.encoding=UTF-8 # 脚本编码.
flyway.ignore-failed-future-migration=true # 在读元数据表时,是否忽略失败的后续迁移.
flyway.init-sqls= # S获取连接后立即执行初始化的SQL语句
flyway.locations=classpath:db/migration # 脚本位置, 默认为classpath: db/migration.
flyway.out-of-order=false # 是否允许乱序(out of order)迁移
flyway.placeholder-prefix= # 设置每个占位符的前缀。 默认值: ${ 。
flyway.placeholder-replacement=true # 是否要替换占位符。 默认值: true 。
flyway.placeholder-suffix=} # 设置占位符的后缀。 默认值: } 。
flyway.placeholders.*= # 设置占位符的值。
flyway.schemas= # Flyway管理的Schema列表,区分大小写。默认连接对应的默认Schema。
flyway.sql-migration-prefix=V # 迁移脚本的文件名前缀。 默认值: V 。
flyway.sql-migration-separator=__ # 迁移脚本的分割符 默认双下划线
flyway.sql-migration-suffix=.sql # 迁移脚本的后缀 默认 .sql
flyway.table=schema_version # Flyway使用的Schema元数据表名称 默认schema_version
flyway.url= # 待迁移的数据库的JDBC URL。如果没有设置,就使用配置的主数据源。
flyway.user= # 待迁移数据库的登录用户。
flyway.password= # 待迁移数据库的登录用户密码。
flyway.validate-on-migrate=true # 在运行迁移时是否要自动验证。 默认值: true 。
二、liquibase
[liquibase](http://www.liquibase.org)
LiquiBase是一个用于数据库重构和迁移的开源工具,通过日志文件的形式记录数据库的变更,然后执行日志文件中的修改,将数据库更新或回滚到一致的状态。它的目标是提供一种数据库类型无关的解决方案,通过执行schema类型的文件来达到迁移。其有点主要有以下:
- 支持几乎所有主流的数据库,如MySQL, PostgreSQL, Oracle, Sql Server, DB2等;
- 支持多开发者的协作维护;
- 日志文件支持多种格式,如XML, YAML, JSON, SQL等;
- 支持多种运行方式,如命令行、Spring集成、Maven插件、Gradle插件等。
快速开始:
1、加入jar包
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.0</version>
</dependency>
2、加入配置
spring.datasource.url=jdbc:mysql://xxxxxx:3306/liquibase_db?useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.liquibase.change-log=classpath:db/changelog.xml
增加文件changelog.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.4.xsd">
<include file="changelog/V1.0.0_20191026_01__addTable.sql" relativeToChangelogFile="true"/>
<!--
<includeAll path="changelog" relativeToChangelogFile="true" />
-->
</databaseChangeLog>
3、加入执行sql (例如:V1.0.0_20191026_01__addTable.sql)
4、启动项目
5、特殊说明
1)配置不开启sql脚本管理
spring.liquibase.enabled=false
2)默认生成了 DATABASECHANGELOG和 DATABASECHANGELOGLOCK 表,这个版本默认是这个表,如果想自己指定schema表的命,可以设置:
第一种:
spring.liquibase.database-change-log-table=order-log
spring.liquibase.database-change-log-lock-table=order-lock
第二种:
@Configuration
public class LiquibaseConfiguration {
/**
* 订单模块Liquibase
*/
@Bean
public SpringLiquibase userLiquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
// 模块Liquibase文件路径
liquibase.setChangeLog("classpath:db/changelog.xml");
liquibase.setDataSource(dataSource);
liquibase.setShouldRun(true);
liquibase.setResourceLoader(new DefaultResourceLoader());
// 覆盖Liquibase changelog表名
liquibase.setDatabaseChangeLogTable("order_changelog_table");
liquibase.setDatabaseChangeLogLockTable("order_changelog_lock_table");
return liquibase;
}
}
3)命名规范 例如:V1.0.0_20191026_01__addTable.sql
- prefix: 可配置,前缀标识,默认值V表示Versioned,R表示Repeatable
- version: 标识版本号,由一个或多个数字构成,数字之间的分隔符可用点.或下划线_
- separator: 可配置,用于分隔版本标识与描述信息,默认为两个下划线__
- description: 描述信息,文字之间可以用下划线或空格分隔
- suffix: 可配置,后续标识,默认为.sql
4)sql脚本尽可能可重复执行
5)创建了两个表:“databasechangelog”和“databasechangeloglock”。
databasechangelog表包含已针对数据库运行的所有语句的列表。
databasechangeloglock表用于确保两台计算机不会同时尝试修改数据库。
6)支持xml格式的sql
<?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.1.xsd">
<changeSet id="1" author="bob">
<createTable tableName="department">
<column name="id" type="int">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="active" type="boolean" defaultValueBoolean="true"/>
</createTable>
</changeSet>
</databaseChangeLog>
7)relativeToChangelogFile说明:
relativeToChangelogFile如果为true,则表示file属性表示的文件路径是相对于根changelog而不是CLASSPATH的,默认为false。
8)
<include>的file属性表示要包含的changelog文件的路径,这个文件可以是LiquiBase支持的任意格式
<includeAll>指定的是changelog的目录,而不是为文件;
9)Liquibase特性
- 不依赖特定数据库,像Hibernate一样支持几乎所有的主流数据库如MySql,Oracle,PostgreSQL,H2等等支持数据库的操作非常丰富,如创建或修改 表(table)、视图(views)、索引(index)以及数据(data)等
- 对数据库的变更描述支持多种格式XML、YAML、JSON等
- 可以记录数据库的变更历史,是通过创建特定的表结果来实现这个功能
- 可以diff两个数据库的差异
- 可以轻松的把比如mysql库导出其他支持数据库的sql脚本
- 支持java命令、Maven、Gradle、Spring Boot集成运行
- 提供了Eclipse和IDEA的数据库重构插件,但是多年未更新不推荐使用
10)规范
ChangeSet id建议使用Flayway的命名格式V
ChangeSet必须填写author
Liquibase禁止对业务数据进行sql操作
所有表,列要加remarks进行注释
已经执行过的ChangeSet严禁修改。
不要随便升级项目liquibase版本,特别是大版本升级。不同版本ChangeSet MD5SUM的算法不一样。
三、 flyway和liquibase比较
1、用Flyway定义数据库迁移过程
1)优点:SQL用起来便捷顺手。
2)缺点:无法跨平台使用。
原因(sql方言): 不同的数据库,比如Oracle,MSSQL,MySQL
它们的SQL会有少量的差别,内置函数也会有点不同
比如top子句,在Oracle里面就不能用,
2.、用Liquibase定义数据库迁移过程
1)优点:可以跨平台使用和它的其他特性
2)缺点:迁移脚本需要花费精力去维护
3、总结
> 两款数据库迁移工具其实定位上是差别的,一般我的倾向是小项目,整体变动不大的用Flyway,而大应用和企业应用用Liquibase更合适。
附录
DELIMITER $$
DROP PROCEDURE IF EXISTS `p_execute_sql_ignore_error`$$
CREATE PROCEDURE `p_execute_sql_ignore_error`(v_sql VARCHAR(3000))
SQL SECURITY INVOKER
COMMENT '执行某个sql语句(忽略错误)'
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SET @r_status = 'error';
SET @r_status = 'success';
SET @v_sql = v_sql;
PREPARE stmt FROM @v_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
评论区