点击上方“芋道源码”,选择“设为星标”
管她前浪,还是后浪?
能浪的浪,才是好浪!
每天 8:55 更新文章,每天掉亿点点头发...
源码精品专栏
“摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/database-version-control/ 「芋道源码」欢迎转载,保留摘要,谢谢!
1. 概述
2. Flyway
3. Liquibase
666. 彩蛋
“本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-20 目录。
原创不易,给点个 Star 嘿,一起冲鸭!
1. 概述
在我们的认知中,我们会使用 SVN 或 Git 进行代码的版本管理。但是,我们是否好奇过,数据库也是需要进行版本管理的呢?
在每次发版的时候,我们可能都会对数据库的表结构进行新增和变更,又或者需要插入一些初始化的数据。而我们的环境不仅仅只有一套,一般来说会有 DEV、UAT、PRED、PROD 四套环境,会对应 DEV、UAT、PROD 三个数据库。
“PROD 环境,一般连接 PROD 数据库,做准生产的验收。
那么,就意味着我们需要对 DEV、UAT、PROD 数据库都做一遍操作。“人,是系统最大的 BUG”。很多时候,我们并不能保证像机器一样,每次都操作都正确,这就导致在不同的环境下,因为数据的每个版本的初始化,带来额外的验收成本。
甚至说,因为我们常常是手动操作 DEV 数据库,没有整理一个完整清单,保证我们在 UAT、PROD 数据库中执行相同的操作。
基于以上种种,如果我们能像管理代码版本一样,来管理我们的数据库版本,是不是这些问题可以得到很好的解决?答案是,绝大多数是的。
目前,技术社区已经提供了很多解决方案。例如说:
Flyway
Liquibase
dbdeploy
SQL Source Control
dbv
MyBatis Migrations
在 Spring Boot 项目中,提供了对 Flyway 和 Liquibase 的内置支持,所以在有数据库版本的需求时,肯定是推荐它们两。
本文,我们会对 Flyway 和 Liquibase 进行入门学习。这样,我们在学习它们的同时,可以有比较直观的使用感受,方便后续我们对它们进行选型。
2. Flyway
“示例代码对应仓库:lab-20-database-version-control-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
文件中,引入相关依赖。
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lab-20-database-version-control-flyway</artifactId>
<dependencies>
<!-- 实现对数据库连接池的自动化配置 -->
<!-- 同时,spring-boot-starter-jdbc 支持 Flyway 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency> <!-- 本示例,我们使用 MySQL -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- Flyway 依赖 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
</dependencies>
</project>
具体每个依赖的作用,胖友自己认真看下艿艿添加的所有注释噢。
2.2 应用配置文件
在 resources
目录下,创建 application.yaml
配置文件。配置如下:
spring:
# datasource 数据源配置内容,对应 DataSourceProperties 配置属性类
datasource:
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 配置内容,对应 FlywayAutoConfiguration.FlywayConfiguration 配置项
flyway:
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
注解即可。代码如下:
// Application.java
@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 约定如下:
“FROM https://flywaydb.org/documentation/migrations#naming-1
![]()
Naming
Pref