关于java:Spring-Boot-2x基础教程使用Flyway管理数据库版本

40次阅读

共计 5823 个字符,预计需要花费 15 分钟才能阅读完成。

之前曾经介绍了很多在 Spring Boot 中应用 MySQL 的案例,蕴含了 Spring Boot 最原始的 JdbcTemplate、Spring Data JPA 以及咱们国内最罕用的 MyBatis。同时,对于一些简单场景比方:更换 Druid 数据源,或是多数据源的状况也都做了介绍。

不管咱们应用哪一个具体实现框架,都离不开对数据库表构造的治理。而这一类治理始终都存在一个问题:因为数据库表元数据存储于数据库中,而咱们的拜访逻辑都存在于 Git 或其余代码仓库中。Git 曾经帮忙咱们实现了代码的多版本治理,那么数据库中的表该如何做好版本控制呢?

明天咱们就来介绍在 Spring Boot 中应用 Flyway 来治理数据库版本的办法。

Flyway 简介

Flyway 是一个简略开源数据库版本控制器(约定大于配置),次要提供 migrate、clean、info、validate、baseline、repair 等命令。它反对 SQL(PL/SQL、T-SQL)形式和 Java 形式,反对命令行客户端等,还提供一系列的插件反对(Maven、Gradle、SBT、ANT 等)。

官方网站:https://flywaydb.org/

本文对于 Flyway 的本身性能不做过多的介绍,读者能够通过浏览官网文档或利用搜索引擎取得更多材料。上面咱们具体说说在 Spring Boot 利用中的利用,如何应用 Flyway 来创立数据库以及构造不统一的查看。

入手试试

上面咱们先预设一个开发指标:

  1. 假如咱们须要开发一个用户管理系统,那么咱们势必要设计一张用户表,并实现对用户表的增删改查操作。
  2. 在工作 1 的性能实现之后,咱们又有一个新需要,须要对用户表减少了一个字段,看看如何实现对数据库表构造的更改。

指标 1 的实现

第一步:创立一个根底的 Spring Boot 我的项目,并在 pom.xml 中退出 Flyway、MySQL 连贯和数据拜访相干的必要依赖(这里选用 spring-boot-starter-jdbc 作为例子)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-core</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

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

第二步:按 Flyway 的标准创立版本化的 SQL 脚本。

  • 在工程的 src/main/resources 目录下创立 db 目录,在 db 目录下再创立 migration 目录
  • migration 目录下创立版本化的 SQL 脚本V1__Base_version.sql
DROP TABLE IF EXISTS user ;
CREATE TABLE `user` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(20) NOT NULL COMMENT '姓名',
  `age` int(5) DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

留神:如果你不想将 SQL 脚本放到其余目录,能够用 spring.flyway.locations 参数来配置。这里不同于 1.x 版本的配置项flyway.locations

第三步:依据 User 表的构造,编写对应的实体定义

@Data
@NoArgsConstructor
public class User {

    private Long id;
    private String name;
    private Integer age;

}

第四步:编写用户操作接口和实现

public interface UserService {

    /**
     * 新增一个用户
     *
     * @param name
     * @param age
     */
    int create(String name, Integer age);

    /**
     * 依据 name 查问用户
     *
     * @param name
     * @return
     */
    List<User> getByName(String name);

    /**
     * 依据 name 删除用户
     *
     * @param name
     */
    int deleteByName(String name);

    /**
     * 获取用户总量
     */
    int getAllUsers();

    /**
     * 删除所有用户
     */
    int deleteAllUsers();}

@Service
public class UserServiceImpl implements UserService {

    private JdbcTemplate jdbcTemplate;

    UserServiceImpl(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}

    @Override
    public int create(String name, Integer age) {return jdbcTemplate.update("insert into USER(NAME, AGE) values(?, ?)", name, age);
    }

    @Override
    public List<User> getByName(String name) {List<User> users = jdbcTemplate.query("select * from USER where NAME = ?", (resultSet, i) -> {User user = new User();
            user.setId(resultSet.getLong("ID"));
            user.setName(resultSet.getString("NAME"));
            user.setAge(resultSet.getInt("AGE"));
            return user;
        }, name);
        return users;
    }

    @Override
    public int deleteByName(String name) {return jdbcTemplate.update("delete from USER where NAME = ?", name);
    }

    @Override
    public int getAllUsers() {return jdbcTemplate.queryForObject("select count(1) from USER", Integer.class);
    }

    @Override
    public int deleteAllUsers() {return jdbcTemplate.update("delete from USER");
    }

}

这里次要介绍 Flyway 的利用,所以采纳这种比较简单的编写形式,理论我的项目利用中,还是举荐 MyBatis 的具体操作实现。

第五步:编写测试用例

@Slf4j
@SpringBootTest
public class Chapter311ApplicationTests {

    @Autowired
    private UserService userSerivce;

    @Test
    public void test() throws Exception {userSerivce.deleteAllUsers();

        // 插入 5 个用户
        userSerivce.create("Tom", 10);
        userSerivce.create("Mike", 11);
        userSerivce.create("Didispace", 30);
        userSerivce.create("Oscar", 21);
        userSerivce.create("Linda", 17);

        // 查问名为 Oscar 的用户,判断年龄是否匹配
        List<User> userList = userSerivce.getByName("Oscar");
        Assertions.assertEquals(21, userList.get(0).getAge().intValue());

        // 查数据库,应该有 5 个用户
        Assertions.assertEquals(5, userSerivce.getAllUsers());

        // 删除两个用户
        userSerivce.deleteByName("Tom");
        userSerivce.deleteByName("Mike");

        // 查数据库,应该有 5 个用户
        Assertions.assertEquals(3, userSerivce.getAllUsers());
    }

}

留神因为 Spring Boot 2.4 利用的 junit 版本与之前 Spring Boot 1.x 版本中的不同,因而单元测试的编写略有区别,有趣味的读者能够别离查看之前介绍文章和这篇文章中的单元测试的区别,这里就不细说了。

第六步:运行下面编写的单元测试,验证一下成果。

不出意外,单元测试运行 ok 的话

连上数据库看看。此时应该多出了这两张表:

  • user表就是咱们保护在 SQL 脚本中要创立的表
  • flyway_schema_history表是 flyway 的治理表,用来记录在这个数据库上跑过的脚本,以及每个脚本的查看根据。这样每次利用启动的时候,就能够晓得哪个脚本须要运行,或者哪个脚本产生了变动,运行根底可能不对,造成数据结构的凌乱而阻止运行。

指标 2 的实现

有了下面的根底之后,咱们来说说后续要做表构造的表变动该怎么操作,这也是之前读者呈现问题最多的状况,所以在 2.x 版本教程中顺便讲一讲。

首先,大家在开始应用 Flyway 之后,对于数据库表接口的变更就要敞开这几个路径:

  1. 间接通过工具登录数据去批改表构造
  2. 曾经公布的 sql 脚本不容许批改

正确的表结构调整路径:在 flyway 脚本配置门路下编写新的脚本,启动程序来执行变更。这样能够取得几个很大的益处:

  1. 脚本受 Git 版本管理控制,能够不便的找到过来的历史
  2. 脚本在程序启动的时候先加载,再提供接口服务,一起实现部署步骤
  3. 所有表构造的历史变迁,在治理目录中依据版本号就能很好的追溯

上面依据一个理论需要来具体操作下。假如咱们当初想对 User 表减少一个字段:address,用来存储用户的通讯地址,那么咱们就须要这样操作实现。

第一步:创立脚本文件 V1_1__alter_table_user.sql,并写入减少address 列的语句

ALTER TABLE `user` ADD COLUMN `address` VARCHAR(20) DEFAULT NULL;

对于脚本文件名的根本规定是:版本号__形容.sql。当然如果你有更粗疏的要求,那么能够做更粗疏的文件名布局,具体细节读者能够查阅文末参考资料中的官网文档获取。

第二步:再次执行单元测试,在控制台中能够看到如下日志:

2021-01-11 16:58:12.025  INFO 37330 --- [main] o.f.c.i.database.base.DatabaseType       : Database: jdbc:mysql://localhost:3306/test (MySQL 8.0)
2021-01-11 16:58:12.063  INFO 37330 --- [main] o.f.core.internal.command.DbValidate     : Successfully validated 2 migrations (execution time 00:00.020s)
2021-01-11 16:58:12.075  INFO 37330 --- [main] o.f.core.internal.command.DbMigrate      : Current version of schema `test`: 1
2021-01-11 16:58:12.082  INFO 37330 --- [main] o.f.core.internal.command.DbMigrate      : Migrating schema `test` to version "1.1 - alter table user"
2021-01-11 16:58:12.113  INFO 37330 --- [main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema `test` (execution time 00:00.045s)

再查看一下数据中国的内容:

如果你还没有领会到引入 Flyway 对给咱们的表构造带来的益处的话,无妨也留言分享下你们的治理形式吧!

更多本系列收费教程连载「点击进入汇总目录」

代码示例

本文的相干例子能够查看上面仓库中的 chapter3-11 目录:

  • Github:https://github.com/dyc87112/SpringBoot-Learning/
  • Gitee:https://gitee.com/didispace/SpringBoot-Learning/

如果您感觉本文不错,欢送 Star 反对,您的关注是我保持的能源!

参考资料

  • Spring Boot 中应用 Flyway 来治理数据库版本
  • Flyway 官网文档

欢送关注我的公众号:程序猿 DD,取得独家整顿的收费学习资源助力你的 Java 学习之路!另每周赠书不停哦~

正文完
 0