【问题标题】:Using liquibase to create triggers in mysql使用 liquibase 在 mysql 中创建触发器
【发布时间】:2022-03-11 17:54:25
【问题描述】:

我想使用 liquibase 在 mysql 中创建一个简单的触发器。以下脚本直接在 mysql 中运行:

delimiter $$

CREATE TRIGGER myTrigger
    BEFORE INSERT ON myTable FOR EACH ROW
BEGIN
    IF(NEW.my_timestamp IS NULL) THEN
        SET NEW.my_timestamp = now();
    END IF;
END$$

delimiter ;

所以,我想创建一个变更集供 liquibase 使用,它可以使用 update 命令应用此触发器,并在使用 updateSQL 命令时创建一个合适的 sql 脚本。

我在变更集中尝试了各种选项,包括 splitStatements 和 endDelimiter,但只能获得与 update 命令或 updateSQL 命令一起工作的东西。两者都没有。

这是一个使用格式化 sql 的示例更改集,当我使用 update 命令时它可以正常工作,但在我使用 updateSQL 命令时不会创建合适的 sql

-- liquibase formatted sql
-- changeset pcoates33:trigger-1 splitStatements:false

CREATE TRIGGER myTrigger
    BEFORE INSERT ON myTable FOR EACH ROW
BEGIN
    IF(NEW.my_timestamp IS NULL) THEN
        SET NEW.my_timestamp = now();
    END IF;
END

-- rollback DROP TRIGGER IF EXISTS myTrigger;

这是我想要的 updateSQL 的工作方式,但更新失败:

-- liquibase formatted sql
-- changeset pcoates33:trigger-1 splitStatements:false

delimiter $$

CREATE TRIGGER myTrigger
    BEFORE INSERT ON myTable FOR EACH ROW
BEGIN
    IF(NEW.my_timestamp IS NULL) THEN
        SET NEW.my_timestamp = now();
    END IF;
END$$

delimiter ;

-- rollback DROP TRIGGER IF EXISTS myTrigger;

基本问题是

  1. mysql 脚本需要分隔符 $$ 和分隔符 ;在里面
  2. 如果 liquibase 的 jdbc 调用在开头有分隔符 $$,它将失败

【问题讨论】:

  • DELIMITER 命令是 CLI 的命令。 liquibase 可能不支持它...
  • @Akina 我知道 - 我试图找到一种方法让 liquibase 在运行更新(即 jdbc 调用)时忽略分隔符语句,但在运行 updateSQL 时包含它。据我所知,使用 liquibase 是不可能的。
  • 看看stackoverflow.com/a/57113256/10138734——也许会有帮助。

标签: mysql liquibase


【解决方案1】:

您的触发器可以很容易地转换为单语句形式:

CREATE TRIGGER myTrigger
BEFORE INSERT 
ON myTable 
FOR EACH ROW
SET NEW.my_timestamp = COALESCE(NEW.my_timestamp, NOW());

在分隔符中不需要重新分配。


如果由于该列根本没有在 INSERT 查询的列列表中列出而将 NULL 插入该列,那么您可以简单地在列定义中使用DEFAULT CURRENT_TIMESTAMP,并且不需要触发器。但是如果显式设置了 NULL 值,则此方法不适用。

【讨论】:

  • 谢谢你 - 我认为我的例子有点太简单了。这确实是一个更通用的问题,我希望能够在触发器中使用多个语句。我同意触发器不是设置时间戳的好主意。
【解决方案2】:

我同意@Akina 的观点,一个好的解决方案是要么不使用触发器,要么将它们压缩成单个语句。

我正在尝试将 liquibase 引入现有数据库,因此希望能够在最初保持相同。然后应用更改以使其更简单。

所以,基本问题是

  1. mysql 脚本需要分隔符 $$ 和 delimiter ;在里面
  2. 如果 liquibase 的 jdbc 调用在开头有分隔符 $$,它将失败

经过多次尝试,我想出的解决方案依赖于使用 gradle 插件来运行 liquibase updateSql 命令。我无法单独使用 liquibase 来实现它。我基本上是注释掉liquibase无法识别的部分代码,然后对liquibase创建的脚本文件进行后期处理,取消注释语句。

这是我的 sql 格式变更集:

-- liquibase formatted sql
-- changeset pcoates33:trigger-1 splitStatements:false stripComments:false
-- delimiter $$

CREATE TRIGGER myTrigger
    BEFORE INSERT ON myTable FOR EACH ROW
BEGIN
    IF(NEW.my_timestamp IS NULL) THEN
        SET NEW.my_timestamp = now();
    END IF;
END-- $$

-- delimiter ;
-- rollback DROP TRIGGER IF EXISTS myTrigger;

我可以使用它运行 liquibase 更新,并且它已正确应用。

我将 gradle 配置为使用 liquibase 插件,并使用 outputFile : "$projectDir/update.sql"

然后更新 build.gradle 文件以扩展插件添加的 updateSql 任务,以更改输出中我想要的语句中的 cmets

updateSQL{
  doLast {
    ant.replace(file: "$projectDir/update.sql", token: '-- delimiter', value: 'delimiter')
    ant.replace(file: "$projectDir/update.sql", token: '-- $$', value: '$$')
  }
}

【讨论】:

    【解决方案3】:

    不用delimiter玩,只要加上changeSet属性splitStatements = "false",即yaml版本为:

    - changeSet:
      changes:
        - sql:
            dbms: 'mysql'
            splitStatements: false
            sql: |
              CREATE TRIGGER myTrigger BEFORE INSERT ON myTable FOR EACH ROW
              BEGIN
                  IF(NEW.my_timestamp IS NULL) THEN
                       SET NEW.my_timestamp = now();
                  END IF;
              END;
    

    【讨论】:

    • 我从事这个项目已经一年多了,但是,我认为问题在于我们有一些环境运行 liquibase update,而另一些环境使用 liquibase updateSql 生成然后运行的 SQL直接针对分贝。正如您在下面的回答中看到的那样,我使用了splitStatements:false,这对于 liquibase 更新用例来说效果很好。但是,除非添加分隔符,否则当您直接针对 mysql db 运行 updateSql 生成的 SQL 时,它将不起作用。
    • 明白你的意思。我的错,我来到这个主题,寻找如何使用常规 liquibase 更新(不是 updateSql)来解决 MySql 触发器的问题。所以 splitStatements=false 对我来说已经足够了。
    猜你喜欢
    • 2015-03-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    • 2011-02-03
    • 2016-09-19
    • 2013-09-16
    相关资源
    最近更新 更多