Knex.JS自动更新触发器

Knex.JS Auto Update Trigger

本文关键字:更新 触发器 JS Knex      更新时间:2023-09-26

我正在使用Knex.JS迁移工具。但是,在创建表时,我希望有一个名为updated_at的列,当数据库中的记录更新时,该列会自动更新。

例如,这里有一个表:

knex.schema.createTable('table_name', function(table) {
    table.increments();
    table.string('name');
    table.timestamp("created_at").defaultTo(knex.fn.now());
    table.timestamp("updated_at").defaultTo(knex.fn.now());
    table.timestamp("deleted_at");
})

created_atupdated_at列默认为创建记录的时间,这很好。但是,当更新该记录时,我希望updated_at列显示它自动更新的新时间。

我宁愿不写原始的postgres。

谢谢!

对于Postgres,您需要一个触发器。这是我成功使用的一种方法。

添加函数

如果您有多个按设置顺序的迁移文件,您可能需要人为地更改文件名中的日期戳,以使其首先运行(或者只将其添加到您的第一个迁移文件中)。如果无法回滚,则可能需要通过psql手动执行此步骤。然而,对于新项目:

const ON_UPDATE_TIMESTAMP_FUNCTION = `
  CREATE OR REPLACE FUNCTION on_update_timestamp()
  RETURNS trigger AS $$
  BEGIN
    NEW.updated_at = now();
    RETURN NEW;
  END;
$$ language 'plpgsql';
`
const DROP_ON_UPDATE_TIMESTAMP_FUNCTION = `DROP FUNCTION on_update_timestamp`
exports.up = knex => knex.raw(ON_UPDATE_TIMESTAMP_FUNCTION)
exports.down = knex => knex.raw(DROP_ON_UPDATE_TIMESTAMP_FUNCTION)

现在,该功能应该可用于所有后续迁移。

定义knex.raw触发器帮助程序

如果可以避免的话,我发现在迁移文件中不重复大块的SQL更具表现力。我在这里使用了knexfile.js,但如果你不想让它复杂化,你可以在任何地方定义它。

module.exports = {
  development: {
    // ...
  },
  production: {
    // ...
  },
  onUpdateTrigger: table => `
    CREATE TRIGGER ${table}_updated_at
    BEFORE UPDATE ON ${table}
    FOR EACH ROW
    EXECUTE PROCEDURE on_update_timestamp();
  `
}

使用辅助对象

最后,我们可以非常方便地定义自动更新触发器:

const { onUpdateTrigger } = require('../knexfile')
exports.up = knex =>
  knex.schema.createTable('posts', t => {
    t.increments()
    t.string('title')
    t.string('body')
    t.timestamps(true, true)
  })
    .then(() => knex.raw(onUpdateTrigger('posts')))
exports.down = knex => knex.schema.dropTable('posts')

请注意,删除该表就足以消除触发器:我们不需要显式的DROP TRIGGER

这一切看起来可能需要做很多工作,但一旦你完成了,它就非常"设置并忘记",如果你想避免使用ORM,它也很方便。

您可以使用时间戳创建knex迁移:

exports.up = (knex, Promise) => {
  return Promise.all([
    knex.schema.createTable('table_name', (table) => {
      table.increments();
      table.string('name');
      table.timestamps(false, true);
      table.timestamp('deleted_at').defaultTo(knex.fn.now());
    })
  ]);
};
exports.down = (knex, Promise) => {
  return Promise.all([
    knex.schema.dropTableIfExists('table_name')
  ]);
};

使用时间戳,将创建一个数据库模式,其中添加一个created_atupdated_at列,每个列都包含一个初始时间戳。

要使updated_at列保持最新,您需要knex.raw:

table.timestamp('updated_at').defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));

要跳过knex.raw解决方案,我建议使用像Objection.js这样的高级ORM。使用Objection.js,您可以实现自己的BaseModel,然后更新updated_at列:

Something.js

const BaseModel = require('./BaseModel');
class Something extends BaseModel {
  constructor() {
    super();
  }
  static get tableName() {
    return 'table_name';
  }
}
module.exports = Something;

基本模型

const knexfile = require('../../knexfile');
const knex = require('knex')(knexfile.development);
const Model = require('objection').Model;
class BaseModel extends Model {
  $beforeUpdate() {
    this.updated_at = knex.fn.now();
  }
}
module.exports = BaseModel;

来源:http://vincit.github.io/objection.js/#timestamps

这是我在Mysql 5.6+中实现这一点的方法

我没有使用table.timestamps的原因是我使用了DATETIME而不是时间戳。

table.dateTime('created_on')
        .notNullable()
        .defaultTo(knex.raw('CURRENT_TIMESTAMP'))
table.dateTime('updated_on')
        .notNullable()
        .defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))

这不是Knex的特性。Knex只创建列,但不会为您更新它们。

但是,如果使用Bookshelf ORM,则可以指定表具有时间戳,并且它将设置&按预期更新列:

  • 书架文档
  • Github问题

exports.up=(knex)=>{返回knex.raw(create or replace function table_name_update() RETURNS trigger AS $$ begin new.updated_at = now(); RETURN NEW; end; $$ language 'plpgsql'; create or replace trigger tg_table_name_update on table_name before update for each row execute table_name_update();)};

exports.down=(knex)=>{返回knex.raw(drop table if exists table_name; drop function if exists table_name_update;)};

您可以直接使用此函数

table.timestamps()

这将在默认情况下创建"created_at"answers"updated_at"列,并相应地更新它们

https://knexjs.org/#Schema-时间戳