java 数据库版本管理_Spring Boot 数据库版本管理Flyway

在 Flyway 的官网 https://flywaydb.org/ 中,对自己的介绍是:

Version control for your database.

数据库的版本管理。

Flyway 支持的数据库,主要是关系数据库。

Flyway 提供了 SQL-based migrations 和 Java-based migrations 两种数据库变更方式。

前者使用简单,无需编写 Java 代码。

后者需要使用 Java 编写代码,胜在灵活。

一般情况下,如果是做表的变更,或者记录的简单插入、更新、删除等操作,使用 SQL-based migrations 即可。

复杂场景下,我们可能需要关联多个表,则需要通过编写 Java 代码,进行逻辑处理,此时就是和使用 Java-based migrations 了。

下面,让我们来使用它们二者,更好的体会它们的区别。

2.1 引入依赖

在 pom.xml 文件中,引入相关依赖。

org.springframework.boot

spring-boot-starter-jdbc

mysql

mysql-connector-java

5.1.48

org.flywaydb

flyway-core

2.2 应用配置文件

在 resources 目录下,创建 application.yaml 配置文件。配置如下:

spring:

datasource://数据源配置内容,对应 DataSourceProperties 配置属性类

url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8

driver-class-name: com.mysql.jdbc.Driver

username: root //数据库账号

password: //数据库密码

flyway:// flyway 配置内容,对应 FlywayAutoConfiguration.FlywayConfiguration 配置项

enabled: true // 开启 Flyway 功能

cleanDisabled: true //禁用 Flyway 所有的 drop 相关的逻辑,避免出现跑路的情况。

locations: //迁移脚本目录

- classpath:db/migration //配置 SQL-based 的 SQL 脚本在该目录下

- classpath:cn.iocoder.springboot.lab20.databaseversioncontrol.migration //配置 Java-based 的 Java 文件在该目录下

check-location: false //是否校验迁移脚本目录下。如果配置为 true ,代表需要校验。此时,如果目录下没有迁移脚本,会抛出 IllegalStateException 异常

url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8 //数据库地址

user: root //数据库账号

password: //数据库密码

spring.datasource 配置项,设置数据源的配置。这里暂时没有实际作用,仅仅是为了项目不报数据源的错误。

spring.flyway 配置项,设置 Flyway 的属性,而后可以被 FlywayAutoConfiguration 自动化配置。

每个配置项的作用,胖友自己看下注释。更多的配置项,可以看看 《Spring Boot 配置属性详解 -- Migration》 文章。

重点看下 locations 配置项,我们分别设置了 SQL 和 Java 迁移脚本的所在目录。

2.3 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication

public class Application {

public static void main(String[] args) {

// 启动 Spring Boot 应用

SpringApplication.run(Application.class, args);

}

}

启动项目。执行日志如下:

// Flyway 的信息

2019-11-16 13:42:34.454 INFO 59115 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.4 by Boxfuse

2019-11-16 13:42:34.619 INFO 59115 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://127.0.0.1:3306/lab-20-flyway (MySQL 8.0)

2019-11-16 13:42:34.643 WARN 59115 --- [ main] o.f.c.i.s.classpath.ClassPathScanner : Unable to resolve location classpath:db/migration

// 发现 0 个迁移脚本。

2019-11-16 13:42:34.657 INFO 59115 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 0 migrations (execution time 00:00.004s)

// 创建 flyway_schema_history 表

2019-11-16 13:42:34.671 INFO 59115 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table: `lab-20-flyway`.`flyway_schema_history`

// 打印当前数据库的迁移版本

2019-11-16 13:42:34.702 INFO 59115 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `lab-20-flyway`: << Empty Schema >>

// 判断,没有需要迁移的脚本

2019-11-16 13:42:34.702 INFO 59115 --- [ main] o.f.core.internal.command.DbMigrate : Schema `lab-20-flyway` is up to date. No migration necessary.

// 启动项目完成

2019-11-16 13:42:34.759 INFO 59115 --- [ main] c.i.s.l.d.Application : Started Application in 1.2 seconds (JVM running for 1.596)`

在启动的日志中,我们看到 Flyway 会自动创建 flyway_schema_history表,记录 Flyway 每次迁移( migration )的历史。表结构如下:

CREATE TABLE `flyway_schema_history` (

`installed_rank` int(11) NOT NULL, -- 安装顺序,从 1 开始递增。

`version` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL, -- 版本号

`description` varchar(200) COLLATE utf8mb4_bin NOT NULL, -- 迁移脚本描述

`type` varchar(20) COLLATE utf8mb4_bin NOT NULL, -- 脚本类型,目前有 SQL 和 Java 。

`script` varchar(1000) COLLATE utf8mb4_bin NOT NULL, -- 脚本地址

`checksum` int(11) DEFAULT NULL, -- 脚本校验码。避免已经执行的脚本,被人变更了。

`installed_by` varchar(100) COLLATE utf8mb4_bin NOT NULL, -- 执行脚本的数据库用户

`installed_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 安装时间

`execution_time` int(11) NOT NULL, -- 执行时长,单位毫秒

`success` tinyint(1) NOT NULL, -- 执行结果是否成功。1-成功。0-失败

PRIMARY KEY (`installed_rank`),

KEY `flyway_schema_history_s_idx` (`success`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

大体看下每个字段的注释,后面对着具体的记录,会更容易理解。

2.4 SQL-based migrations

在 resources/db/migration 目录下,创建 V1.0__INIT_DB.sql SQL 迁移脚本。如下:

-- 创建用户表

CREATE TABLE `users` (

`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',

`username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',

`password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',

`create_time` datetime DEFAULT NULL COMMENT '创建时间',

PRIMARY KEY (`id`),

UNIQUE KEY `idx_username` (`username`)

) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- 插入一条数据

INSERT INTO `users`(username, password, create_time) VALUES('yudaoyuanma', 'password', now());

比较简单,就是创建用户表 users 表,并往里面插入一条记录。

重点在于 V1.0__INIT_DB.sql 的命名上。Flyway 约定如下:

Prefix 前缀:V 为版本迁移,U 为回滚迁移,R 为可重复迁移。

在我们的示例中,我们使用 V 前缀,表示版本迁移。绝大多数情况下,我们只会使用 V 前缀。

Version 版本号:每一个迁移脚本,都需要一个对应一个唯一的版本号。而脚本的执行顺序,按照版本号的顺序。一般情况下,我们使用数字自增即可。

在我们的示例中,我们使用 1.0 。

Separator 分隔符:两个 _ ,即 __ 。可配置,不过一般不配置。

Description 描述:描述脚本的用途。

在我们的示例中,我们使用 INIT_DB 。

Suffix 后缀:.sql 。可配置,不过一般不配置。

我们再次启动 Application 项目。执行日志如下:

// Flyway 的信息

2019-11-16 14:20:25.893 INFO 59615 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.4 by Boxfuse

2019-11-16 14:20:26.063 INFO 59615 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://127.0.0.1:3306/lab-20-flyway (MySQL 8.0)

// 发现一个迁移脚本,就是 V1.0__INIT_DB.sql 。

2019-11-16 14:20:26.096 INFO 59615 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 1 migration (execution time 00:00.013s)

// 打印当前数据库的迁移版本

2019-11-16 14:20:26.137 INFO 59615 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `lab-20-flyway`: << Empty Schema >>

// 开始迁移到版本 1.0

2019-11-16 14:20:26.138 INFO 59615 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `lab-20-flyway` to version 1.0 - INIT DB

// 可以忽略,MySQL 报的告警日志

2019-11-16 14:20:26.148 WARN 59615 --- [ main] o.f.c.i.s.DefaultSqlScriptExecutor : DB: Integer display width is deprecated and will be removed in a future release. (SQL State: HY000 - Error Code: 1681)

// 成功执行一个迁移

2019-11-16 14:20:26.157 INFO 59615 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 1 migration to schema `lab-20-flyway` (execution time 00:00.049s)

// 启动项目完成

2019-11-16 14:20:26.214 INFO 59615 --- [ main] c.i.s.l.d.Application : Started Application in 1.236 seconds (JVM running for 1.638)

此时,我们去查询下 MySQL 。如下:

mysql> show tables;

+-------------------------+

| Tables_in_lab-20-flyway |

+-------------------------+

| flyway_schema_history |

| users |

+-------------------------+

2 rows in set (0.00 sec)

如上,我们可以看到两个表。

其中,users 表,就是我们需要在 V1.0__INIT_DB.sql 迁移脚本中,需要创建的。

mysql> SELECT * FROM users;

+----+-------------+----------+---------------------+

| id | username | password | create_time |

+----+-------------+----------+---------------------+

| 7 | yudaoyuanma | password | 2019-11-16 14:21:32 |

+----+-------------+----------+---------------------+

1 row in set (0.00 sec)

users 表的该记录,就是我们希望插入的一条记录。

mysql> SELECT * FROM flyway_schema_history;

+----------------+---------+-------------+------+-------------------+-------------+--------------+---------------------+----------------+---------+

| installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success |

+----------------+---------+-------------+------+-------------------+-------------+--------------+---------------------+----------------+---------+

| 1 | 1.0 | INIT DB | SQL | V1.0__INIT_DB.sql | -1362702755 | root | 2019-11-16 14:21:32 | 12 | 1 |

+----------------+---------+-------------+------+-------------------+-------------+--------------+---------------------+----------------+---------+

1 row in set (0.00 sec)

flyway_schema_history 表中,增加了一条版本号为 1.0 的,使用 V1.0__INIT_DB.sql 迁移脚本的日志。

看下每个操作,以及其注释。

我们再再次启动 Application 项目。执行日志如下:

// Flyway 的信息

2019-11-16 14:30:10.925 INFO 59715 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.4 by Boxfuse

2019-11-16 14:30:11.089 INFO 59715 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://127.0.0.1:3306/lab-20-flyway (MySQL 8.0)

// 发现一个迁移脚本,就是 V1.0__INIT_DB.sql 。

2019-11-16 14:30:11.127 INFO 59715 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 1 migration (execution time 00:00.014s)

// 打印当前数据库的迁移版本为 `1.0`

2019-11-16 14:30:11.137 INFO 59715 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `lab-20-flyway`: 1.0

// 判断已经到达最新版本,无需执行迁移

2019-11-16 14:30:11.137 INFO 59715 --- [ main] o.f.core.internal.command.DbMigrate : Schema `lab-20-flyway` is up to date. No migration necessary.

// 启动项目完成

2019-11-16 14:30:11.196 INFO 59715 --- [ main] c.i.s.l.d.Application : Started Application in 1.141 seconds (JVM running for 1.528)

下面,我们注释掉 V1.0__INIT_DB.sql 迁移脚本中的,INSERT 操作。我们再再再次启动 Application 项目。会报如下错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version 1.0

-> Applied to database : -1362702755

-> Resolved locally : -883795183`

Flyway 会给每个迁移脚本,计算出一个 checksum 字段。这样,每次启动时,都会校验已经安装( installed )的迁移脚本,是否发生了改变。如果是,抛出异常。这样,保证不会因为脚本变更,导致出现问题。

2.5 Java-based migrations

在 cn.iocoder.springboot.lab20.databaseversioncontrol.migration 包路径下,创建 V1_1__FixUsername.java 类,修复 users 的用户名。代码如下:

public class V1_1__FixUsername extends BaseJavaMigration {

private Logger logger = LoggerFactory.getLogger(getClass());

@Override

public void migrate(Context context) throws Exception {

// 创建 JdbcTemplate ,方便 JDBC 操作

JdbcTemplate template = new JdbcTemplate(context.getConfiguration().getDataSource());

// 查询所有用户,如果用户名为 yudaoyuanma ,则变更成 yutou

template.query("SELECT id, username, password, create_time FROM users", new RowCallbackHandler() {

@Override

public void processRow(ResultSet rs) throws SQLException {

// 遍历返回的结果

do {

String username = rs.getString("username");

if ("yudaoyuanma".equals(username)) {

Integer id = rs.getInt("id");

template.update("UPDATE users SET username = ? WHERE id = ?",

"yutou", id);

logger.info("[migrate][更新 user({}) 的用户名({} => {})", id, username, "yutou");

}

} while (rs.next());

}

});

}

@Override

public Integer getChecksum() {

return 11; // 默认返回,是 null 。

}

@Override

public boolean canExecuteInTransaction() {

return true; // 默认返回,也是 true

}

@Override

public MigrationVersion getVersion() {

return super.getVersion(); // 默认按照约定的规则,从类名中解析获得。可以自定义

}

}

比较简单,胖友看下代码注释。这里仅仅是示例,实际迁移的逻辑,会更加复杂。

Java 迁移脚本,可以通过类名按照和 「2.4 SQL-based migrations」 一样的命名约定,自动获得版本号。当然,也可以通过重写 #getVersion() 方法,自定义版本号。

我们再再再再次启动 Application 项目。执行日志如下:

// Flyway 的信息

2019-11-16 14:45:30.733 INFO 59941 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.4 by Boxfuse

2019-11-16 14:45:30.907 INFO 59941 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://127.0.0.1:3306/lab-20-flyway (MySQL 8.0)

// 发现一个迁移脚本,就是 V1.0__INIT_DB.sql 和 V1_1__FixUsername.java

2019-11-16 14:45:30.946 INFO 59941 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.014s)

// 打印当前数据库的迁移版本为 `1.0`

2019-11-16 14:45:30.956 INFO 59941 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `lab-20-flyway`: 1.0

// 开始迁移到版本 1.1

2019-11-16 14:45:30.957 INFO 59941 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `lab-20-flyway` to version 1.1 - FixUsername

2019-11-16 14:45:30.977 INFO 59941 --- [ main] c.i.s.l.d.migration.V1_1__FixUsername : [migrate][更新 user(7) 的用户名(yudaoyuanma => yutou)

// 成功执行一个迁移

2019-11-16 14:45:30.985 INFO 59941 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 1 migration to schema `lab-20-flyway` (execution time 00:00.034s)

// 启动项目完成

2019-11-16 14:45:31.039 INFO 59941 --- [ main] c.i.s.l.d.Application : Started Application in 1.221 seconds (JVM running for 1.61)

此时,我们去查询下 MySQL 。如下:

mysql> SELECT * FROM users;

+----+-------------+----------+---------------------+

| id | username | password | create_time |

+----+-------------+----------+---------------------+

| 7 | yutou | password | 2019-11-16 14:21:32 |

+----+-------------+----------+---------------------+

1 row in set (0.00 sec)

users 表的该记录,用户名被修改为 yutou 。

mysql> SELECT * FROM flyway_schema_history;

+----------------+---------+-------------+------+--------------------------------------------------------------------------------+-------------+--------------+---------------------+----------------+---------+

| installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success |

+----------------+---------+-------------+------+--------------------------------------------------------------------------------+-------------+--------------+---------------------+----------------+---------+

| 1 | 1.0 | INIT DB | SQL | V1.0__INIT_DB.sql | -1362702755 | root | 2019-11-16 14:21:32 | 12 | 1 |

| 2 | 1.1 | FixUsername | JDBC | cn.iocoder.springboot.lab20.databaseversioncontrol.migration.V1_1__FixUsername | 11 | root | 2019-11-16 14:45:30 | 19 | 1 |

+----------------+---------+-------------+------+--------------------------------------------------------------------------------+-------------+--------------+---------------------+----------------+---------+

2 rows in set (0.00 sec)

flyway_schema_history 表中,增加了一条版本号为 1.1 的,使用 V1_1__FixUsername.sql 迁移脚本的日志。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值