使用 liquibase 在 mysql 中创建触发器

Using liquibase to create triggers in mysql

我想使用 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 使用的变更集,它可以使用更新命令应用此触发器,并且还会在使用 updateSQL 命令时创建一个合适的 sql 脚本。

我已经尝试了变更集中的各种选项,包括 splitStatements 和 endDelimiter,但只能获得适用于 update 命令或 updateSQL 命令的内容。两者都不行。

这是一个使用格式化 sql 的示例更改集,当我使用更新命令时它工作正常,但当我使用 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 调用以分隔符 $$ 开头,则调用将失败

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

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

不需要重新分配分隔符。


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

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

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

所以,基本问题是

  1. mysql 脚本需要分隔符 $$ 和分隔符 ;其中
  2. 如果 liquibase 的 jdbc 调用以分隔符 $$ 开头,则调用将失败

经过多次尝试,我想出的解决方案依赖于使用 gradle 插件到 运行 liquibase updateSql 命令。我无法单独使用 liquibase 来实现它。我基本上注释了 liquibase 无法识别的代码部分,然后 post 处理 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 任务,以更改输出中我想要的语句的注释

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

不需要玩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;