创建或更新 Sequelize

Posted

技术标签:

【中文标题】创建或更新 Sequelize【英文标题】:Create or Update Sequelize 【发布时间】:2013-08-20 16:49:38 【问题描述】:

我在我的 Nodejs 项目中使用 Sequelize,我发现了一个我很难解决的问题。 基本上我有一个 cron 从服务器获取对象数组,然后将其作为对象插入到我的数据库中(对于这种情况,卡通)。但如果我已经拥有其中一个对象,我必须对其进行更新。

基本上我有一个对象数组,并且可以使用 BulkCreate() 方法。但是当 Cron 再次启动时,它并没有解决它,所以我需要一些带有 upsert true 标志的更新。主要问题是:在所有这些创建或更新之后,我必须有一个只触发一次的回调。有谁知道我该怎么做?迭代一个对象数组..创建或更新它,然后得到一个回调?

感谢关注

【问题讨论】:

【参考方案1】:

从docs 获取对象后,您无需查询where 即可执行更新。此外,promise 的使用应该简化回调:

实施

function upsert(values, condition) 
    return Model
        .findOne( where: condition )
        .then(function(obj) 
            // update
            if(obj)
                return obj.update(values);
            // insert
            return Model.create(values);
        )

用法

upsert( first_name: 'Taku' ,  id: 1234 ).then(function(result)
    res.status(200).send(success: true);
);

注意

    此操作不是原子操作。 创建 2 个网络调用。

这意味着建议重新考虑该方法,并且可能只更新一次网络调用中的值,或者:

    查看返回的值(即 rows_affected)并决定做什么。 如果更新操作成功则返回成功。这是因为资源是否存在不在此服务的职责范围内。

【讨论】:

返回 Model.create(...values, ...condition) 处理高频数据时,非原子性会导致数据重复发生!否则,一个优雅的解决方案!【参考方案2】:

您可以使用upsert 这更容易。

实现细节:

mysql - 作为单个查询实现INSERT values ON DUPLICATE KEY UPDATE values PostgreSQL - 实现为带有异常处理的临时函数:INSERT EXCEPTION WHEN unique_constraint UPDATE SQLite - 实现为两个查询INSERT; UPDATE。这意味着无论该行是否已经执行更新 存在与否 MSSQL - 使用MERGE and WHEN (NOT) MATCHED THEN 作为单个查询实现注意 SQLite 返回未定义的创建,没有 无论该行是创建还是更新。这是因为 SQLite 总是在一个查询中运行INSERT OR IGNORE + UPDATE,所以有 无法知道该行是否已插入。

【讨论】:

使用 upsert,但如果它执行了插入,那么您需要获取插入的对象 :( - ***.com/a/29071422/48348 @IanGrainger - 不再! ***.com/a/50301416/107277【参考方案3】:

更新 07/2019 现在使用 async/await

async function updateOrCreate (model, where, newItem) 
    // First try to find the record
   const foundItem = await model.findOne(where);
   if (!foundItem) 
        // Item not found, create a new one
        const item = await model.create(newItem)
        return  item, created: true;
    
    // Found an item, update it
    const item = await model.update(newItem, where);
    return item, created: false;

我喜欢 Ataik 的想法,但把它缩短了一点:

function updateOrCreate (model, where, newItem) 
    // First try to find the record
    return model
    .findOne(where: where)
    .then(function (foundItem) 
        if (!foundItem) 
            // Item not found, create a new one
            return model
                .create(newItem)
                .then(function (item)  return  item: item, created: true; )
        
         // Found an item, update it
        return model
            .update(newItem, where: where)
            .then(function (item)  return item: item, created: false ) ;
    

用法:

updateOrCreate(models.NewsItem, slug: 'sometitle1', title: 'Hello World')
    .then(function(result) 
        result.item;  // the model
        result.created; // bool, if a new item was created.
    );

可选:在此处添加错误处理,但我强烈建议将一个请求的所有承诺链接起来,并在最后使用一个错误处理程序。

updateOrCreate(models.NewsItem, slug: 'sometitle1', title: 'Hello World')
    .then(..)
    .catch(function(err));

【讨论】:

评论:我注意到 taht update() .. 不返回数据库对象。所以如果你需要它,你可以再拿一次。【参考方案4】:

这可能是一个老问题,但这就是我所做的:

var updateOrCreate = function (model, where, newItem, onCreate, onUpdate, onError) 
    // First try to find the record
    model.findOne(where: where).then(function (foundItem) 
        if (!foundItem) 
            // Item not found, create a new one
            model.create(newItem)
                .then(onCreate)
                .catch(onError);
         else 
            // Found an item, update it
            model.update(newItem, where: where)
                .then(onUpdate)
                .catch(onError);
            ;
        
    ).catch(onError);

updateOrCreate(
    models.NewsItem, title: 'sometitle1', title: 'sometitle',
    function () 
        console.log('created');
    ,
    function () 
        console.log('updated');
    ,
    console.log);

【讨论】:

提示:使用承诺链进行错误处理并节省 50% 的代码 :) 父子关系怎么用?如何更新子表?【参考方案5】:
User.upsert( a: 'a', b: 'b', username: 'john' )

它将尝试通过第一个参数中的哈希查找记录以更新它,如果找不到 - 然后将创建新记录

Here 是 sequelize 测试中的使用示例

it('works with upsert on id', function() 
    return this.User.upsert( id: 42, username: 'john' ).then(created => 
        if (dialect === 'sqlite') 
            expect(created).to.be.undefined;
         else 
            expect(created).to.be.ok;
        

        this.clock.tick(1000);
        return this.User.upsert( id: 42, username: 'doe' );
    ).then(created => 
        if (dialect === 'sqlite') 
            expect(created).to.be.undefined;
         else 
            expect(created).not.to.be.ok;
        

        return this.User.findByPk(42);
    ).then(user => 
        expect(user.createdAt).to.be.ok;
        expect(user.username).to.equal('doe');
        expect(user.updatedAt).to.be.afterTime(user.createdAt);
    );
);

【讨论】:

它将在唯一或主键列上找到匹配项 我真的很喜欢回答问题的方式。荣誉【参考方案6】:

听起来您想将 Sequelize 调用包装在 async.each 中。

【讨论】:

我希望在 Sequelize 的更新中提供一些令人不安的选项,但它似乎超出了路线图。我会试试这个。谢谢!【参考方案7】:

这可以通过自定义事件发射器来完成。

假设您的数据位于名为 data 的变量中。

new Sequelize.Utils.CustomEventEmitter(function(emitter) 
    if(data.id)
        Model.update(data, id: data.id )
        .success(function()
            emitter.emit('success', data.id );
        ).error(function(error)
            emitter.emit('error', error );
        );
     else 
        Model.build(data).save().success(function(d)
            emitter.emit('success', d.id );
        ).error(function(error)
            emitter.emit('error', error );
        );
    
).success(function(data_id)
    // Your callback stuff here
).error(function(error)
   // error stuff here
).run();  // kick off the queries

【讨论】:

【参考方案8】:

您可以在 sequelize 中使用 findOrCreateupdate 方法。这是一个带有 async.js 的示例

async.auto(
   getInstance : function(cb) 
      Model.findOrCreate(
        attribute : value,
        ...
      ).complete(function(err, result) 
        if (err) 
          cb(null, false);
         else 
          cb(null, result);
        
      );
    ,
    updateInstance : ['getInstance', function(cb, result) 
      if (!result || !result.getInstance) 
        cb(null, false);
       else 
        result.getInstance.updateAttributes(
           attribute : value,
           ...
        , ['attribute', ...]).complete(function(err, result) 
          if (err) 
            cb(null, false);
           else 
            cb(null, result);
          
        );
       
      ]
     , function(err, allResults) 
       if (err || !allResults || !allResults.updateInstance) 
         // job not done
        else 
         // job done
     );
);

【讨论】:

【参考方案9】:

这是一个简单的例子,它要么更新 deviceID -> pushToken 映射,要么创建它:

var Promise = require('promise');
var PushToken = require("../models").PushToken;

var createOrUpdatePushToken = function (deviceID, pushToken) 
  return new Promise(function (fulfill, reject) 
    PushToken
      .findOrCreate(
        where: 
          deviceID: deviceID
        , defaults: 
          pushToken: pushToken
        
      )
      .spread(function (foundOrCreatedPushToken, created) 
        if (created) 
          fulfill(foundOrCreatedPushToken);
         else 
          foundOrCreatedPushToken
            .update(
              pushToken: pushToken
            )
            .then(function (updatedPushToken) 
              fulfill(updatedPushToken);
            )
            .catch(function (err) 
              reject(err);
            );
        
      );
  );
;

【讨论】:

以上是关于创建或更新 Sequelize的主要内容,如果未能解决你的问题,请参考以下文章

创建、编辑或删除新行后表格不更新

我应该使用 POST 还是 PUT 进行可以创建或更新的 API 调用

续集更新或创建包含的关联

Laravel 5.1 创建或更新重复

python drf查看mixin以更新不存在的对象(例如,更新或创建然后更新)

如何创建表的回滚副本以防我插入或更新错误