提示:本文中,我们只给了部分示例代码。如果你需要完整的代码,请点击: https://github.com/mengyunzhi/springBootSampleCode/tree/master/flyway

WHY TO DO

在使用flyway的版本迁移功能时,如果我们并不是在项目之初就启用flyway的话,那么在有历史数据的情况下,启用flyway后,由于数据库中,并不存在flyway所依赖的库,所以将会出现:set baselineOnMigrate to true to initialize the schema history table的错误。

本文环境

java:1.8 + spring-boot:2.0.3.RELEASE + mysql:5.6

知识准备

官方文档:

解决方案

  1. 新建一个数据库,并将ddl-auto设置为create, 然后启用flyway
  2. 得到一张flyway需要使用的数据表,并导出为scheme.sql文件。
  3. 将导出的scheme.sql文件,导入到历史数据库中。

启用flyway

建立flyway数据库

mysql中建立flyway数据库。
clipboard.png

加入flyway依赖

pom.xml中加入flyway

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mengyunzhi.springBootSampleCode</groupId>
    <artifactId>flyway</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>flyway</name>
    <description>在历史项目上使用flyway做版本迁移控制</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

建立升级sql

resources中建立db/migration文件夹,并建立V1.0__init.sql(是__而不是_)。

clipboard.png

V1.0__init.sql

# 这是一行注释,防止文件为空。

生成数据表

spring:
  jpa:
    hibernate:
      ddl-auto: create
  datasource:
    username: root
    password:
    url: jdbc:mysql://127.0.0.1/flywayDemo?useUnicode=true&characterEncoding=utf-8

起动应用,此时,将在数据库中,生成一张数据表。

clipboard.png

导出数据表结构

在数据表上右键 -> 转存为sql文件 -> 仅结构。然后我们在需要使用flyway的历史数据库中,将其导入即可。

测试

我们保持数据为不动,把当前项目模拟为历史项目(ddl-auto: update), 更改配置为:

spring:
  jpa:
    hibernate:
      ddl-auto: update
  datasource:
    username: root
    password:
    url: jdbc:mysql://127.0.0.1/flywayDemo?useUnicode=true&characterEncoding=utf-8

应用成功启动,未报错。

优化方案

上述方案,是可行的,但不够自动化。我们期待自动化的解决数据表的升级问题,而不是手工去做1,2,3,4..项操作。原因很简单,当我们进行数据升级时,心情大都会比较紧张,就怕出错。但越紧张就越容易出错,如果我们今天手动改一点,记本上,明天手动改一点,再记本上。系统一升级,手工进行1-12项改动,难免就会发生错误。所以,我们要避免手动进行升级可能会发生错误的尴尬。

下面,我们启用spring boot在初始化数据方法,在初始化数据时,增加一个函数。在函数中实现:如果有了flyway的数据表,则跳过。如果没有flyway的数据表,则执行语句生成数据表。

启用数据初始化

spring:
  jpa:
    hibernate:
      ddl-auto: update
  datasource:
    username: root
    password:
    url: jdbc:mysql://127.0.0.1/flywayDemo?useUnicode=true&characterEncoding=utf-8
#    设置数据初始化模式为:always
    initialization-mode: always     
#    设置 ; 的重写符号
    separator: //

clipboard.png

-- 重写 ; 为 // ,在spring中,注释掉下面一行,应该我们在配置文件中的 separator: // 便是起的该作用
-- DELIMITER //
-- 如果存在函数,则先删除
DROP PROCEDURE IF EXISTS `FUN20180706` //
-- 定义函数FUN20180706
CREATE PROCEDURE `FUN20180706` ()
    BEGIN
        DECLARE hasDataTable INT;
        SELECT count(*) INTO hasDataTable FROM information_schema.tables WHERE (table_schema = 'flywayDemo') AND (table_name= 'flyway_schema_history');
        IF hasDataTable = 0 THEN
           CREATE TABLE `flyway_schema_history` (
              `installed_rank` int(11) NOT NULL,
              `version` varchar(50) DEFAULT NULL,
              `description` varchar(200) NOT NULL,
              `type` varchar(20) NOT NULL,
              `script` varchar(1000) NOT NULL,
              `checksum` int(11) DEFAULT NULL,
              `installed_by` varchar(100) NOT NULL,
              `installed_on` timestamp NOT NULL DEFAULT current_timestamp(),
              `execution_time` int(11) NOT NULL,
              `success` tinyint(1) NOT NULL,
              PRIMARY KEY (`installed_rank`),
              KEY `flyway_schema_history_s_idx` (`success`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
          SET FOREIGN_KEY_CHECKS = 1;
        END IF;
    END
//

-- 调用函数
CALL FUN20180706() //
-- 恢复重写的;,以免影响其它的function
-- DELIMITER ;

测试

  1. 删除原来生成的数据表flyway_schema_history
  2. 启动项目

项目正常启动,并生成了flyway的数据表。而且,该数据表中,添加了1.0版本的初始化文件。成功!

我们接着测试:

  1. 删除原来生成的数据表flyway_schema_history
  2. schema.sql更名为schema1.sql
  3. 启动项目

clipboard.png

报没有找到历史数据表的错误,不错,这正是我们期待的,这说明,生成flyway所需要的数据表,的确是schema.sql的功劳。

扩展

在进行开发时,如果我们使用的为h2等嵌入式数据库,每次数据表都重新生成的话,需要禁用自动执行schema1.sql,即需要将initialization-mode:设置为none

总结

我们通过查看官方文档,结合mysql创建函数的知识,解决了在历史表中,自动生成flyway所需要的数据表的目标。从而使项目上线后,使用flyway更新数据表,成为了可能。

对于初级软件工程师的我们,我们的需求其实大牛们早就解决了。成功的秘诀便是:与成功者共舞。尝试看懂、应用官方文档,今天你行动了吗?

路漫漫其修远兮,吾将上下而求索。

河北工业大学梦云智软件开发团队,期待你的加入!


潘杰
3.1k 声望238 粉丝