拆分Liquibase更改日志? 没问题。

What is Liquibase?

As Wikipedia says:

Liquibase是一个独立于开源数据库的库,用于跟踪,管理和应用数据库模式更改。 它始于2006年,以便更轻松地跟踪数据库更改,尤其是在敏捷软件开发环境中。

我发现Liquibase是一种自动迁移数据库的简洁工具。 无论如何,数据库迁移本身是一个非常复杂的话题,超出了本文的范围。

Liquibase可以作为独立工具运行,也可以集成到您的应用程序中。 将其添加到Spring上下文很容易。

Spring Boot使它变得更加容易。 如果在属性文件中启用了Liquibase,则会自动配置Liquibase,并且在类路径中有Liquibase,并且有数据源在上下文中。

liquibase.change-log=classpath:changelog.xml
liquibase.enabled=true

Liquibase也使MockMvc测试非常简单。 可以将其配置为创建H2数据库以进行测试。

How Liquibase works?

Liquibase读取xml changelog文件并找出原因变更集它需要申请。 它使用数据库更改日志数据库中的表(数据源) 以此目的。 的数据库更改日志 contains the list of 变更集 that are already applied with their ID,文件名,MD5SUM,作者和其他一些属性。

逻辑相对简单。 仅通过将变更日志与表进行比较,Liquibase便知道需要应用哪些变更集。 但是,很少有陷阱...

  • Liquibase只能使用一个变更日志文件Liquibase在应用变更集之前确定要应用的变更集列表实际的数据库可能与数据库更改日志表。 例如。 如果您手动修改数据库,等等如果Liquibase无法应用变更集,它将立即失败,并且不会继续处理下一个数据集如果Liquibase作为bean在Spring应用程序中运行,它将在应用程序启动期间执行,因此,如果失败,则该应用程序将无法启动变更集不是原子的。 变更集的一部分可能会通过,它会正确修改数据库,而下一部分会失败。 变更集记录不会进入数据库更改日志表。 因此,它使数据库处于需要手动修复的状态(例如,还原更改集的一部分并让Liquibase重新运行)变更集无法修改。 如果在将变更集应用到数据库后修改变更集,则Liquibase无法声明MD5SUM不匹配。该ID不是变更集的唯一标识符。 实际上是ID,文件名和作者中的变更集数据库更改日志和are not in the changelog files are ignored

Of course, Liquibase has much more functionality. Just read the documentation. This is also out of scope of this article.

The changelog file grows over time

是的,如果您一开始没有定义策略,那么您的变更日志文件就会越来越大。 在一个大型项目中,它可能需要成千上万行,包含数百个变更集。 变更日志文件上的代码流失率也很高,因此会引起一些合并工作。

为了避免这种情况,您应该尽早考虑一些替代方案。

Define multiple changelog files

..和<include>它们在主变更日志中,例如:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd">

    <include file="feature1.xml" relativeToChangelogFile="true"/>
    <include file="feature2.xml" relativeToChangelogFile="true"/>
    <include file="feature3.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

这样做的好处是显而易见的-减少了代码改动,改善了组织。 如果文件之间的变更集之间存在任何逻辑依赖性,则会出现问题-例如 多个文件的表之间是否定义了任何关系。 由于Liquibase按顺序执行变更集,因此它以feature1.xml,继续feature2.xml。

也许您可以根据目标版本找到更好的拆分键?

    <include file="release_0.1.0.0.xml" relativeToChangelogFile="true"/>
    <include file="release_0.1.0.1.xml" relativeToChangelogFile="true"/>
    <include file="release_1.0.0.0.xml" relativeToChangelogFile="true"/>

Configure multiple Liquibase runs

由于一次运行只能获取一个变更日志文件,因此只需定义多个变更日志文件并让Liquibase运行多次即可。

在您的Spring(Boot)应用中,只需定义多个液基豆子:

import liquibase.integration.spring.SpringLiquibase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;

import javax.sql.DataSource;

@Configuration
public class MultipleLiquiaseConfiguration {

    @Bean
    public SpringLiquibase liquibaseRelease1(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:release_v1.xml");

        return liquibase;
    }

    @Bean
    public SpringLiquibase liquibaseRelease2(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:release_v2.xml");

        return liquibase;
    }
}

将在上下文中创建两个bean,因此将执行2次Liquibase运行。 如果您依靠Spring Boot的自动配置,则您的实体管理器豆会迫使你有一个叫液基。 这很容易做到。 另外,如果您的变更日志需要按特定顺序运行,则可以通过以下方法解决此问题@取决于:

@Configuration
public class MultipleLiquiaseConfiguration {

    @Bean
    public SpringLiquibase liquibaseV1(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:release_v1.xml");

        return liquibase;
    }

    @Bean
    @DependsOn("liquibaseV1")
    public SpringLiquibase liquibase(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:release_v2.xml");

        return liquibase;
    }
}

请注意,最后运行的称为液基(您的实体管理器取决于),它指向上一个要运行的@取决于注解。

How to deal with long changelog?

如果您早期没有应用任何策略,或者只是使用旧版代码加入了一个正在运行的项目,则您的变更日志已经太大了。 现在,如何减少呢?

您可能会说-好吧,我只是将其拆分为多个文件,并使用上述两种策略中的任何一种。 好吧,不是那么快! :)前面我提到文件名很重要,因为文件名用于确定是否应用了变更集。 如果仅将现有变更集移动到另一个文件,Liquibase会认为这些变更集未应用,并且实际上将尝试再次应用它们。 并且它将失败,因为数据库已经包含了更改。

为了更好地描述该问题,请想象一个具有这种情况的模型情况changelog.xml:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd">

    <changeSet author="me" id="changeset1">
        <createTable tableName="TABLE1">
            <column name="COLUMN1" type="VARCHAR2(10)"/>
        </createTable>
    </changeSet>

    <changeSet author="me" id="changeset2">
        <createTable tableName="TABLE2">
            <column name="COLUMN1" type="VARCHAR2(10)"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

然后将第二个变更集移至changelog2.xml并包括changelog2.xml在里面changelog.xml。 启动您的应用程序将失败,并出现类似的异常:

Table "TABLE1" already exists; 

好的,由于该数据库是从头开始创建的,因此在单元测试中可以正常工作,但是如果您运行Liquibase来迁移已部署实例的数据库,它将失败。 我们都同意这是不好的;)

幸运的是,我们还有几个选择;)

Change the logicalFilePath

Liquibase允许您定义变更日志的所谓逻辑文件路径。 这使您可以伪造Liquibase,这些变更集实际上来自同一文件。 想象一下changelog2.xml现在看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd"
                   logicalFilePath="classpath:changelog.xml">

    <changeSet author="me" id="changeset2">
        <createTable tableName="TABLE2">
            <column name="COLUMN1" type="VARCHAR2(10)"/>
        </createTable>
    </changeSet>
</databaseChangeLog>

注意logicalFilePath在那里有价值。 是的,这会起作用,Liquibase会解决这个问题变更集2就像以前在changelog.xml。 完善。

实际上,此方法也有一些缺点,这些缺点可能(但可能不会)阻止您申请。 如果您不将变更日志存储在资源中,而是存储在文件系统中的其他位置,则您的数据库更改日志将包含文件的完整路径。 如果随后有多个要迁移数据库的环境,并且更改日志文件的位置有所不同,则无法设置logicalFilePath。 请记住,它必须与先前的值匹配。

另一个问题是,如果要拆分变更日志的目的是将变更日志的一部分移至另一个软件包,模块等,则此方法不是最佳方法。

Use intermediate changelog

如果您打算将变更日志的一部分移至另一个模块(例如,您最终希望将您那讨厌的整体打破成几个拥有自己数据库的微服务),那么这种方法可能是您的最佳选择。 它包含一些中间和临时步骤,但结果是您想要的:)

第一步是将所有相关的变更集移动到其他位置的另一个文件中。 在上面的示例中,我们只是移动了变更集2至changelog2.xml. Now we need至fake Liquibase that those changesets didn't change. We do it by 修改文件名数据库中的值作为Liquibase更改日志本身的一部分;)

再创建一个(中级/临时)变更日志(我们称之为tmp-migration.xml)与这个变更集:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd">

    <changeSet id="moving changesets from changelog to changelog2" author="Maros Kovacme">
        <sql>
            UPDATE DATABASECHANGELOG
            SET
            FILENAME = REPLACE(FILENAME, 'changelog.xml', 'changelog2.xml'))
            WHERE
            ID IN (
            'changeset2'
            );
        </sql>
    </changeSet>

</databaseChangeLog>

此变更集将从以下位置替换数据库中的文件名列值:classpath:changelog.xml至classpath:changelog2.xml。 然后,当我们使用changelog2.xml, it will think that all changesets are already applied. It is not possible至use just 2 changelog files for this purpose. Liquibase first calculates the list of changesets至be applied (per changelog file) and only then it will apply them. We need至modify the 文件名在处理第二个文件之前。

我们必须应用的最后一步是在上下文中以正确的顺序定义相应的bean:

@Configuration
public class MultipleLiquiaseConfiguration {

    @Bean
    public SpringLiquibase liquibaseChangelog(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:changelog.xml");

        return liquibase;
    }

    @Bean
    @DependsOn("liquibaseChangelog")
    public SpringLiquibase liquibaseMigration(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:tmp-migration.xml");

        return liquibase;
    }

    @Bean("liquibase")
    @DependsOn("liquibaseMigration")
    public SpringLiquibase liquibaseChangelog2(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:changelog2.xml");

        return liquibase;
    }
}

的changelog.xml will run first. 的changeset2 exists in the 数据库更改日志但不在文件中,因此将被忽略。 然后tmp-migration.xml运行并更改文件名 column. 的last will run the changelog2.xml,但Liquibase会将changeet2视为已应用。

稍后(当您认为所有受影响的数据库都已迁移时),您可以删除tmp-migration.xml和它在一起。 变更集将保留在数据库更改日志桌子,但这只是我认为的一件小事。

然后,下一步可能是将bean的定义移至具体微服务的上下文中。

Conclusion

总有某种方法;)

from: https://dev.to//vladonemo/splitting-liquibase-changelong-no-problem-2a4l

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值