数据库版本控制Flyway+Maven的具体实现

来源:互联网 发布:javaweb书籍推荐知乎 编辑:程序博客网 时间:2024/04/27 05:55
1.  引言
想到要管理数据库的版本,是在实际产品中遇到问题后想到的一种解决方案,当时各个环境的数据库乱作一团,没有任何一个人(开发、测试、维护人员)能够讲清楚当前环境下的数据库是哪个版本,与哪个版本的应用相匹配,如何升级到与新版本的应用相匹配。
想到管理数据库版本时,先是心底形成了一个初步的解决方案,大致是通过数据库中的某张表来记录数据库表结构的历次更新与对应版本,在每次数据库表结构调整时除了提供库表更新sql ,还必须提供更新记录与对应版本的sql,以帮助维护数据库版本信息,并在遇到问题时提供相关的排查依据。
后来据此思路在网络上搜索了一把,结果搜到Liquibase (另一款开源数据库版本管理工具)。在学习了解Liquibase 的时候,经高手介绍又了解到了Flyway 这个项目的存在。经过一番了解,最后我们选择了Flyway ,主要原因是Flyway 支持sql 脚本,而Liquibase 只支持XML 方式的数据库表结构定义,虽然在新的版本中号称在XML 的数据库表结构定义方式中支持了sql 脚本。
虽然Flyway 的中文文档近乎为零,英文文档也凤毛麟角,但它却是我们最理想的数据库版本管理工具,它不但支持sql 脚本,还支持Java 代码直接操作数据库(在版本升级时做数据迁移相当有用),有Maven 插件,支持命令行(我们的平台数据库有部分由C 语言项目管理),而且在Spring 框架的配合下,很容易就能实现应用启动时自动检查并升级数据库的功能。

2.  什么是Flyway
Flyway 是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。
Flyway 的项目主页是 http://flywaydb.org/ ),在项目的主页上可以看到Flyway 与几款主流数据库版本管理工具的特性对比列表。

3.  为什么使用Flyway
3.1. 我们遇到的问题
我们遇到的问题(以下内容来自Flyway 的一些英文文档,从中抽取出来的我们也遇到的一些主要问题):
  • 不同的开发人员在开发产品特性时,都有可能更新数据库(添加新表,新的约束等)。当开发人员完成工作并提交代码时,代码会被合并到主分支并在测试服务器上执行单元测试与集成测试。我们在哪个环节来执行数据库的更新操作呢?由QA 部门手工执行sql 脚本?或者我们开发一断程序自动执行数据库更新?以什么顺序来执行这些更新脚本?这些问题同样存在于生产环境。 
  • 我们的产品部署在不同的客户服务器上,以及很多的测试、联调、实验局、销售环境上。不同的客户和测试环境上都部署着不同版本的产品。当他们需要升级他们的产品到新的版本时,我们不仅需要让他们的管理员可以升级产品到新的版本,同时需要保留他们的已有数据。在升级产品的步骤中,我们清楚地知道客户数据库的当前版本,以及需要在该数据库上执行哪些数据库更新脚本,来更新数据库表结构与数据库中已存在的数据。当升级完成时,数据库表结构及数据应当与升级后的产品版本保持一致。
  • 有的时候,我们需要通过代码(Java )来维护一些已存在的数据,如通过代码来维护blob 类型的用户头像数据。
  • 当升级失败时(比如在升级过程中出现网络连接失败),我们应当支持对失败进行修复。
3.2. Flyway 的特性
  • 自动升级(自动发现更新项):Flyway 会将任意版本的数据库升级到最新版本。Flyway 可以脱离JVM 环境通过命令行执行,可以通过Ant 脚本执行,通过Maven 脚本执行(这样就可以在集成环境自动执行),并且可以在应用中执行(比如在应用启动时执行)。
  • 规约优于配置:Flyway 有一套默认的规约,所以不需要修改任何配置就可以正常使用。
  • 既支持SQL 脚本,又支持Java 代码:可以使用SQL 脚本执行数据库更新,也可以使用Java 代码来进行一些高级数据升级操作。
  • 高可靠性:在集群环境下进行数据库升级是安全可靠的。
  • 支持清除已存在的库表结构:Flyway 可以清除已存在的库表结构,可以从零开始搭建您的库表结构,并管理您的数据库版本升级工作。
  • 支持失败修复。新的2.0 版本提供了repair 功能,用于解决数据库更新操作失败问题。
  • 结合我们遇到的问题,与Flyway 所提供的特性,我们认为Flyway 是比较适合于我们的一款数据库版本管理工具。

4.  如何使用Flyway
使用Flyway ,我们需要准备Flyway 将要执行的数据库脚本(Flyway 支持sql 脚本与java 代码,这里认为在Flyway 下执行数据库更新操作的java 代码也是一种数据库脚本),然后通过Flyway 提供的几种不同运行方式来执行这些脚本。(以下配置参数说明基于Flyway 4.1.0 版本)

4.1. 数据库脚本
Flyway 的主要任务是管理数据库的版本更新,在Flyway 中称每次数据库更新为一个migration ,为了更顺口,我们下面称之为数据库脚本。Flyway 支持SQL-based migrations 和Java-based migrations 。
Flyway 支持的数据库脚本有sql 脚本与java 代码,sql 脚本即普通的sql 脚本,包含创建数据库、表,更新库表结构,数据插入、更新、删除等sql 语句,java 代码则是通过一个有效的数据源,使用java 语言来进行数据库的操作,这里针对的读者是对数据库操作有一定熟悉程度的群体,不再详细讲解如何编写数据库脚本。
Flyway 的sql 脚本与java 代码都遵循以下默认规约:
4.1.1.  Flyway 默认规约
  • SQL 脚本文件默认位置是项目的源文件夹下的db/migration  目录。
  • Java 代码默认位于db.migration  包。
  • SQL 脚本文件及Java 代码类名必须遵循以下命名规则:V<version>[_<SEQ>][__description] 。版本号的数字间以小数点(. )或下划线(_ )分隔开,版本号与描述间以连续的两个下划线(__ )分隔开。如V1_1_0__Update.sql 。Java 类名规约不允许存在小数点,所以Java 类名中版本号的数字间只能以下划线进行分隔。

4.2. Flyway 的几种运行方式
本章主要讲解我们常用的三种Flyway 的执行方式,Flyway 除了提供这三种执行方式外,还提供Ant 任务方式的执行方式,在此我们仅介绍基于MAVEN的运行方式。
4.2.1.  命令行方式
通过命令行方式运行Flyway ,需要下载flyway-commandline 版本并解压到本地,然后flyway (Windows下flyway.cmd ,Linux 下flyway.sh )命令执行Flyway 相关操作。

4.2.2. MAVEN方式
由于公司使用的是编译工具是Maven,集成与Jenkins。因此,在此推荐的最佳方式是将插件集成在POM.xml文件中。注:使用时只需直接在pom中声明flyway的坐标即可。并不需要提前安装。(笔者在此处被坑了不少时间)

执行Maven 命令进行Flyway 操作(下面列出几种常用的操作)
  • mvn flyway:init (初始化Flyway metadata )
  • mvn flyway:migrate (执行Flyway 升级操作)
  • mvn flyway:validate (校验Flyway 数据正确性)
  • mvn clean install flyway:migrate(清理,打包编译project)

我们先在src/main/db/migration下创建存储过程语句
我们有两个SQL文件,第一个是DDL文件,内容如下:

[root@develop02 migration]# cat V1__Create_person_table.sql
create table PERSON (
ID int not null auto_increment,
NAME varchar(80) not null,
primary key (ID)
);

第二个是DML(Data Manipulation Language)文件,向第一个文件插入三条数据
[root@develop02 migration]# cat V2__Insert_persons.sql
INSERT INTO PERSON (NAME) VALUES ('Peter Meyer');
INSERT INTO PERSON (NAME) VALUES ('Peter Bonnd');
INSERT INTO PERSON (NAME) VALUES ('Klara Korn');

首先我们看一下整个的MAVEN项目结构:
[root@develop02 helloworld]# tree
.
├── pom.xml
├── README.md
├── src
│   ├── main
│   │   ├── java
│   │   │   └── helloworld
│   │   │   └── App.java
│   │   └── resources
│   │   └── db
│   │   └── migration
│   │   ├── V1__Create_person_table.sql
│   │   └── V2__Insert_persons.sql
│   └── test
│   └── java
│   └── helloworld
│   └── AppTest.java

完整POM.xml文件如下,笔者连的是mysql数据库,请自行在数据库中添加账号并授权。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>helloworld</groupId>
<artifactId>helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>4.1.0</version>
<configuration>
<user>admin</user>
<password>123456</password>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/mydb</url>
<baseDir>db/migration</baseDir>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
</dependency>
</dependencies>
</project>

现在,我们来通过mvn来触发Flyway。在这个例子中,以前本身紧跟在Maven install动作之后。Flyway使用classpath扫描:迁移脚本必须复制到目标目录中才能被成功应用。Maven调用命令:
mvn clean install flyway:migrate
执行完成后,让我们来查看数据库
use mydb;
mysql> show tables;
+----------------+
| Tables_in_mydb |
+----------------+
| PERSON |
| schema_version |
+----------------+

mysql> select * from schema_version;
+----------------+---------+---------------------+------+-----------------------------+-------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success |
+----------------+---------+---------------------+------+-----------------------------+-------------+--------------+---------------------+----------------+---------+
| 1 | 1 | Create person table | SQL | V1__Create_person_table.sql | -183199919 | admin | 2017-02-14 17:00:46 | 19 | 1 |
| 2 | 2 | Insert persons | SQL | V2__Insert_persons.sql | -1229956836 | admin | 2017-02-14 17:00:46 | 21 | 1 |
+----------------+---------+---------------------+------+-----------------------------+-------------+--------------+---------------------+----------------+---------+

通过schema_version可以看到每次的数据库版本更新信息。

在查看PERSON的表结构,V2的SQL语句已经被成功的插入到了表中。
mysql> select * from PERSON;
+----+-------------+
| ID | NAME |
+----+-------------+
| 1 | Peter Meyer |
| 2 | Peter Bonnd |
| 3 | Klara Korn |
+----+-------------+

我们可以通过mvn flyway:info来查看数据库版本控制的情况
[root@develop02 helloworld]# mvn flyway:info
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- flyway-maven-plugin:4.1.0:info (default-cli) @ helloworld ---
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
[INFO] Flyway 4.1.0 by Boxfuse
[INFO] Database: jdbc:mysql://localhost:3306/mydb (MySQL 5.6)
[INFO]
+---------+---------------------+---------------------+---------+
| Version | Description | Installed on | State |
+---------+---------------------+---------------------+---------+
| 1 | Create person table | 2017-02-15 07:00:46 | Success |
| 2 | Insert persons | 2017-02-15 07:00:46 | Success |
+---------+---------------------+---------------------+---------+

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.253 s
[INFO] Finished at: 2017-02-16T14:15:36+08:00
[INFO] Final Memory: 8M/20M
[INFO] ------------------------------------------------------------------------

结论:
flyway作为一款数据库版本控制插件,功能实现非常简单,能够支持目前市场上主流的数据库,虽然不能够进行数据库的回滚操作,但数据库存储过程语句理应作为代码的一部分,理应集成在项目中,除了查看之前数据库存储过程语句的情况,从而更够实现整个代码生命周期中的自动化集成及部署。

注:在实际使用该工具的初期,网上虽然已有大量资料,但由于笔者对于maven并不十分熟悉,因此也遇到了许多问题。特结合自己在实际使用过程中的情况以及现有资料做了一番整理。希望能够帮助大家在工作中对其使用。

0 0
原创粉丝点击