使用Liquibase和Spring Boot进行数据库迁移的一站式指南 - reflectoring


Liquibase有助于自动化数据库迁移,而Spring Boot使使用Liquibase更加容易。该指南提供了有关如何在Spring Boot应用程序中使用Liquibase的详细信息以及一些最佳实践。
本文随附GitHub上的工作代码示例。
数据库迁移工具可帮助我们跟踪,版本控制和自动执行数据库架构更改。它们帮助我们在不同环境中拥有一致的架构。

Liquibase简介
Liquibase不仅使用简单的旧SQL脚本,而且还使用不同的抽象的,与数据库无关的格式(包括XML,YAML和JSON)来促进数据库迁移。当我们使用非SQL格式进行数据库迁移时,Liquibase会为我们生成特定于数据库的SQL。它照顾不同数据库的数据类型和SQL语法的变化。它支持大多数流行的关系数据库
Liquibase允许通过Liquibase扩展对其当前支持的数据库进行增强。这些扩展也可用于添加对其他数据库的支持。
让我们看一下Liquibase的核心概念:

  • ChangeSet:changeSet是一组需要应用于数据库的更改。Liquibase在ChangeSet级别跟踪更改的执行。
  • Change变更:变更描述了需要应用于数据库的单个变更。Liquibase开箱即用提供了几种更改类型,例如“创建表”或“删除列”,它们都是对SQL的抽象。
  • Changelog变更日志:具有需要应用的数据库变更集列表的文件称为变更日志。这些变更日志文件可以采用SQL,YAML,XML或JSON格式。
  • Preconditions前提条件: 前提条件用于控制变更日志或变更集的执行。它们用于定义需要在其下执行changeSets或change日志的数据库状态。
  • Context上下文:changeSet可以用上下文表达式标记。在给定特定上下文的情况下,Liquibase将评估此表达式以确定是否应在运行时执行changeSet。您可以将上下文表达式与环境变量进行比较。
  • Labels标签:标签目标类似上下文。区别在于changeSet用标签(而不是表达式)列表标记,并且在运行时,我们可以传递标签表达式来选择与表达式匹配的changeSet。
  • Changelog参数:Liquibase允许我们在changelog中使用占位符,它在运行时会动态替换。

Liquibase会创建两个表,databasechangelog 并databasechangeloglock在第一次在数据库中运行时创建。它使用该databasechangelog表来跟踪databasechangeloglockchangeSets 的执行状态,并防止Liquibase并发执行。有关更多详细信息,请参考文档

Spring Boot的Liquibase
默认情况下,当我们将Liquibase依赖项添加到构建文件中时,Spring Boot会自动配置Liquibase 。
Spring Boot使用主数据库DataSource运行Liquibase.如果我们需要使用其他名称DataSource,可以将该bean标记为@LiquibaseDataSource。
另外,我们可以设置spring.liquibase.[url,user,password]属性,以便spring自己创建一个数据源,并使用它来自动配置Liquibase。
默认情况下,Spring Boot在应用程序启动时自动运行Liquibase数据库迁移。
在类路径中名为db/migration的文件夹中查找主变更日志文件db.changelog-master.yaml。如果要使用其他Liquibase变更日志格式或使用不同的文件命名约定,则可以将spring.liquibase.change-log应用程序属性配置为指向其他主变更日志文件。
例如,要用db/migration/my-master-change-log.json作主变更日志文件,我们在中设置以下属性application.yml:

spring:
  liquibase:
    changeLog: "classpath:db/migration/my-master-change-log.json"

主变更日志可以包括其他变更日志,以便我们可以按逻辑步骤将变更拆分。

运行我们的第一个数据库迁移
设置完所有内容后,让我们创建我们的第一个数据库迁移。在本示例中,我们将创建数据库表user_details。
让我们创建一个具有名称的文件db.changelog-master.yaml并将其放置在中src/main/resources/db/changelog:

databaseChangeLog:
  - include:
      file: db/changelog/db.changelog-yaml-example.yaml

主文件只是一个包含集合的集合,这些集合指向具有实际更改的更改日志。
接下来,我们使用第一个实际的变更集创建变更日志,并将其放入文件中src/main/resources/db/changelog-yaml-example.yaml:

databaseChangeLog:
  - changeSet:
      id: create-table-user
      author: liquibase-demo-service
      preConditions:
        - onFail: MARK_RAN
          not:
            tableExists:
              tableName: user_details
      changes:
        - createTable:
            columns:
              - column:
                  autoIncrement: true
                  constraints:
                    nullable: false
                    primaryKey: true
                    primaryKeyName: user_pkey
                  name: id
                  type: BIGINT
              - column:
                  constraints:
                    nullable: false
                  name: username
                  type: VARCHAR(250)
              - column:
                  constraints:
                    nullable: false
                  name: first_name
                  type: VARCHAR(250)
              - column:
                  name: last_name
                  type: VARCHAR(250)
            tableName: user_details

我们使用了changeType createTable,它抽象了表的创建。Liquibase将基于我们的应用程序使用的数据库将上述changeSet转换为适当的SQL。
在执行此更改之前,preCondition检查user_details表是否不存在。如果该表已经存在,则Liquibase会将changeSet标记为已成功运行而不实际运行。
现在,当我们运行Spring Boot应用程序时,Liquibase执行changeSet,该表将创建user_details表,user_pkey作为主键。

使用变更日志参数
当我们要为不同的环境使用不同的值替换占位符时,Changelog参数非常有用。我们可以使用application属性设置这些参数spring.liquibase.parameters,该属性采用键/值对的映射:

spring:
  profiles: docker
  liquibase:
    parameters:
      textColumnType: TEXT
    contexts: local
---
spring:
  profiles: h2
  liquibase:
    parameters:
      textColumnType: VARCHAR(250)
    contexts: local    

我们设置Liquibase参数textColumnType来VARCHAR(250)时春启动的启动h2配置文件,并TEXT当它在启动docker配置文件(假定泊坞窗型材启动一个“真正”的数据库)。
现在,我们可以在变更日志中使用此参数:

databaseChangeLog:
  - changeSet:
     ...
      changes:
        - createTable:
            columns:
             ...
              - column:
                  constraints:
                    nullable: false
                  name: username
                  type: ${textColumnType}

现在,当Spring Boot应用程序在docker配置文件中运行时,它将TEXT用作列类型,并在h2配置文件中使用VARCHAR(250)。

使用Liquibase上下文
上下文可用于控制应运行哪些changeSet。让我们用它在test和local环境中添加测试数据:

<databaseChangeLog>
  <changeSet 
    author="liquibase-docs" 
    id=
"loadUpdateData-example" 
    context=
"test or local">
    <loadUpdateData
      encoding=
"UTF-8"
      file=
"db/data/users.csv"
      onlyUpdate=
"false"
      primaryKey=
"id"
      quotchar=
"'"
      separator=
","
      tableName=
"user_details">
    </loadUpdateData>
  </changeSet>
</databaseChangeLog>

我们正在使用该表达式test or local,它可以在这些上下文中运行,但不适用于生产环境。
现在,我们需要使用属性将上下文传递给Liquibase spring.liquibase.contexts:

---
spring:
  profiles: docker
  liquibase:
    parameters:
      textColumnType: TEXT
    contexts: test

在Spring Boot中配置Liquibase
下面是Spring Boot提供的用于配置Liquibase行为的所有属性的列表。

  •  
  • spring.liquibase.changeLog    主变更日志配置路径。默认为classpath:/db/changelog/db.changelog-master.yaml,
  • spring.liquibase.contexts    要使用的运行时上下文的逗号分隔列表。
  • spring.liquibase.defaultSchema    用于托管数据库对象和Liquibase控制表的模式。
  • spring.liquibase.liquibaseSchema    Liquibase控制表的架构。
  • spring.liquibase.liquibaseTablespace    用于Liquibase对象的表空间。
  • spring.liquibase.databaseChangeLogTable    指定用于跟踪更改历史记录的其他表。默认值为DATABASECHANGELOG。
  • spring.liquibase.databaseChangeLogLockTable    指定用于跟踪并发Liquibase使用情况的其他表。默认值为DATABASECHANGELOGLOCK。
  • spring.liquibase.dropFirst    指示是否在运行迁移之前删除数据库架构。请勿在生产中使用!默认值为false。
  • spring.liquibase.user    登录用户名以连接到数据库。
  • spring.liquibase.password    登录密码以连接到数据库。
  • spring.liquibase.url    要迁移的数据库的JDBC URL。如果未设置,则使用主要配置的数据源。
  • spring.liquibase.labels    运行liquibase时要使用的标签表达式。
  • spring.liquibase.parameters    参数映射将传递给Liquibase。
  • spring.liquibase.rollbackFile    执行更新时将回滚SQL写入的文件。
  • spring.liquibase.testRollbackOnUpdate    在执行更新之前是否应该测试回滚。默认值为false。

在Spring Boot中启用Liquibase的日志记录
INFO为Liquibase 启用级别日志记录将有助于查看Liquibase在应用程序启动期间执行的changeSet。它还有助于识别应用程序尚未启动,因为它在启动过程中正在等待获取变更日志锁。
在其中添加以下应用程序属性application.yml以启用INFO日志:

logging:
  level:
    "liquibase" : info


使用Liquibase的最佳实践

  • 组织变更日志:创建一个主变更日志文件,该文件没有实际的变更集,但包含其他变更日志(仅YAML,JSON和XML支持(包括include),而SQL不包含)。这样做使我们可以将changeSets组织在不同的changelog文件中。每次我们向需要数据库更改的应用程序添加新功能时,我们都可以创建一个新的变更日志文件,将其添加到版本控制中,并将其包含在主变更日志中。
  • 每个ChangeSet只能进行一次更改:每个changeSet只能进行一次更改,因为在应用changeSet失败的情况下,这可以使回滚更加容易。
  • 不要修改ChangeSet:一旦更改集执行完毕,就不要再对其进行修改。相反,如果需要修改现有changeSet所应用的更改,则添加新的changeSet。Liquibase跟踪已执行的changeSet的校验和。如果修改了已经运行的changeSet,默认情况下,Liquibase将无法再次运行该changeSet,并且不会继续执行其他changeSet。
  • ChangeSet Id:Liquibase允许我们为changeSets使用描述性名称。最好使用唯一的描述性名称作为changeSetId,而不要使用序列号。它们使多个开发人员可以添加不同的changeSet,而不必担心他们需要为changeSetId选择的下一个序列号。
  • 参考数据管理:使用Liquibase来填充应用程序所需的参考数据和代码表。这样做可以一起部署所需的应用程序和配置数据。Liquibase提供了changeType loadUpdateData支持此功能。
  • 使用前提条件:具有changeSets的前提条件。他们确保Liquibase在应用更改之前检查数据库状态。
  • 测试迁移:在将其应用于实际的非生产或生产环境之前,请确保始终测试本地编写的迁移。始终使用Liquibase在非生产或生产环境中运行数据库迁移,而不是手动执行数据库更改。

在Spring Boot应用程序启动期间自动运行Liquibase,可以轻松地将应用程序代码更改和数据库更改一起提供。但是,在向具有大量数据的现有数据库表添加索引的情况下,应用程序可能需要更长的时间才能启动。一种选择是预发布数据库迁移(在需要它的代码之前释放数据库更改)并异步运行它们

其他运行Liquibase的方法
Liquibase除了支持Spring Boot集成外,还支持一系列其他选项来运行数据库迁移:


Liquibase有一个Java API,我们可以在任何基于Java的应用程序中使用它来执行数据库迁移。