1.写在前面
对于现在公司的系统多为很多个数据库,对于数据库的管理,发版来说是很复杂了,我们目前是通过每次发增量脚本,每次迭代初期发全量脚本,对于收集全量脚本对测试来说是一件很复杂的事,所以我们采用了和git,svn相似的数据库版本管理工具flyway.
2.使用
2.1 flyway特性
自动升级(自动发现更新项):Flyway 会将任意版本的数据库升级到最新版本。Flyway 可以脱离JVM 环境通过命令行执行,可以通过Ant 脚本执行,通过Maven 脚本执行(这样就可以在集成环境自动执行),并且可以在应用中执行(比如在应用启动时执行)。
规约优于配置:Flyway 有一套默认的规约,所以不需要修改任何配置就可以正常使用。
既支持SQL 脚本,又支持Java 代码:可以使用SQL 脚本执行数据库更新,也可以使用Java 代码来进行一些高级数据升级操作。
高可靠性:在集群环境下进行数据库升级是安全可靠的。
支持清除已存在的库表结构:Flyway 可以清除已存在的库表结构,可以从零开始搭建您的库表结构,并管理您的数据库版本升级工作。
支持失败修复。新的2.0 版本提供了repair 功能,用于解决数据库更新操作失败问题。
2.2使用
使用flyway我们只需要准备要升级的数据库sql文件,然后可以使用flyway的多种方式来运行。
2.2.1. 命令行方式
通过命令行方式运行Flyway ,需要下载flyway-commandline 版本并解压到本地,然后flyway (Windows 下flyway.cmd ,Linux 下flyway.sh )命令执行Flyway 相关操作。
下图是flyway-commandline-1.7 解压后的目录结构:
命令行方式的特点与规约
无需安装JVM ,Maven ,Ant
默认读取conf/flyway.properties 中的配置信息,如果在命令行中指定参数,命令行中指定的参数将覆盖配置文件中的配置
还可以通过参数-configFlie=myFlyway.properties 来重新指定flyway 配置文件,可以通过-configFileEncoding=GBK 来指定配置文件的编码格式
可以将打包好的java 迁移文件放到jars/ 目录下让flyway 可以找到并运行
数据库驱动包(jar )放到jars/ 目录下
sql 脚本文件放到sql/ 目录中
命令行方式运行的配置及使用方法
修改conf/flyway.properties 配置文件
拷贝数据库jdbc 驱动jar 到jars/ 目录
在sql/ 目录下创建配置好的sql 脚本文件目录路径,如flyway 默认的sql 文件路径为db/migration ,我们就需要在sql/ 目录下创建/db/migration 目录结构
将数据库维护脚本放到创建好的sql 脚本文件目录中(维护脚本文件名需要遵循命名规范)
在命令行执行命令(从flyway 安装目录开始执行)flyway init (初始化Flyway metadata )、flyway migrate (执行Flyway 升级操作)、flyway validate (校验Flyway 数据正确性)
2.2.2. Maven 插件
配置Maven 插件
<plugin>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>1.7</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
</dependencies>
<configuration>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost/flywaydemo?useUnicode=true&characterEncoding=utf-8</url>
<user>root</user>
<password></password>
<!-- 设置接受flyway进行版本管理的数据库,多个数据库以逗号分隔 -->
<schemas>flywaydemo</schemas>
<!-- 设置存放flyway metadata数据的表名 -->
<table>schema_version</table>
<!-- 设置flyway扫描sql升级脚本、java升级脚本的目录路径或包路径 -->
<locations>
<location>flyway/migrations</location>
<location>com.kedacom.flywaydemo.migrations</location>
</locations>
<!-- 设置sql脚本文件的编码 -->
<encoding>UTF-8</encoding>
<!-- 设置执行migrate操作之前的validation行为 -->
<validationMode>ALL</validationMode>
<!-- 设置当validation失败时的系统行为 -->
<validationErrorMode>FAIL</validationErrorMode>
</configuration>
</plugin>
上面的插件配置包含了几方面的配置信息:
声明插件
声明数据库驱动的依赖包
Flyway 配置——数据库连接配置
Flyway 配置——Flyway 参数与行为配置
执行Maven 命令进行Flyway 操作(下面列出几种常用的操作)
mvn flyway:init (初始化Flyway metadata )
mvn flyway:migrate (执行Flyway 升级操作)
mvn flyway:validate (校验Flyway 数据正确性)
4.2.3. 在应用启动时自动运行(结合Spring )
定义在应用启动时自动运行Flyway 的Java 类,并实现其逻辑代码
public class FlywayMigration {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void migrate() {
Flyway flyway = new Flyway();
flyway.setDataSource(dataSource);
flyway.setSchemas("flywaydemo"); // 设置接受flyway进行版本管理的多个数据库
flyway.setTable("schema_version"); // 设置存放flyway metadata数据的表名
flyway.setLocations("flyway/migrations", "com.kedacom.flywaydemo.migrations"); // 设置flyway扫描sql升级脚本、java升级脚本的目录路径或包路径
flyway.setEncoding("UTF-8"); // 设置sql脚本文件的编码
flyway.setValidationMode(ValidationMode.ALL); // 设置执行migrate操作之前的validation行为
flyway.setValidationErrorMode(ValidationErrorMode.FAIL); // 设置当validation失败时的系统行为
flyway.migrate();
}
}
在Spring 中根据上面实现的类来定义(实例化)一个bean
<bean id="flywayMigration" class="com.kedacom.flywaydemo.FlywayMigration" init-method="migrate">
<property name="dataSource" ref="dataSource" />
</bean>
从上面的bean 定义中我们可以看到,我们为flywayMigration 这个bean 实例注入了一个数据源,Flyway 的所有操作将针对这个数据源进行;同时我们通过init-method 属性指定了Spring 在实例化该bean 以后,主动执行该bean 的migrate 方法,而该方法内会执行Flyway 更新数据库的操作。
至此,我们达到了在应用启动时,Spring 实例化上下文的时候,在Spring 实例化flywayMigration 这个bean 的时候,自动执行Flyway 更新数据库的操作。
但是,我们还没有达到目的,万一Flyway 还在更新数据库,没有完成更新操作之前,应用程序的其他逻辑已经开始使用数据库进行其他操作了,会导致应用程序产生很多bug ,甚至根本运行不起来。
要解决这个问题,我们可以利用Spring 的bean 依赖原理,让关键的数据库操作bean 依赖于flywayMigration 这个bean ,达到在flywayMigration 没有实例化完成(数据库更新操作完成)之前,不能进行任何其他数据库相关操作。
利用Spring 的bean 依赖让flywayMigration 优先处理数据库更新操作
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" depends-on="flywayMigration">
<property name="dataSource" ref="dataSource" />
</bean><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" depends-on="flywayMigration">
<property name="dataSource" ref="dataSource" />
</bean>