如何正确更新Bookshelf.js的模型?

我确定我错过了一些东西,但是我发现Bookshelf API对我来说是无情的混淆。 这是我想要做的:

  • 我有一个名为Radio的模型,其中包含一个名为serial的应用程序分配的string主键,就本例而言,这两个字段分别是example1example2 。 我已经在模型定义中用idAttribute: 'serial'指定了自定义ID。
  • 我试图用Bookshelf执行upsert(不直接与Knex,在我的实际应用程序,查询变得相当复杂)。
  • 我有插入案例工作,但似乎无法得到更新案例的工作。
  • 为了简单起见,我现在不关心交易或primefaces性。 我很满意得到一个简单的select→插入/更新工作。

具体在这个例子中:

  • 插入设置example1example2
  • 更新设置example1并保持example2不变。

所以我在Bookshelf模型中有这样的一个类,例如“static”方法(“info”包含字段“serial”,“example1”和“example2”):

 insertOrUpdate: function (info) { return new Radio({'serial':info.serial}).fetch().then(function (model) { if (model) { model.set('example1', info.example1); return model.save({}, { method: 'update', patch: true }) } else { return new Radio({ serial: info.serial, example1: info.example1, example2: info.example2 }).save({}, { method: 'insert' }) } }).then(function (model) { console.log("SUCCESS"); }).catch(function (err) { console.log("ERROR", err); }); } 

示例调用:

 Radio.insertOrUpdate({ serial: ..., example1: ..., example2: ... }) 

我遇到的问题是,虽然“插入”的情况下,“更新”的情况下失败:

 ERROR { Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where `serial` = '123223'' at line 1 

打开Knexdebugging时显而易见的是,生成的查询缺lessset子句:

 update `radios` set where `serial` = ? 

现在,我专注于书架文档的fetchsave ,我想知道如果我走错了方向。

我知道我使用的API错误,但我无法弄清楚。 我注意到的一些奇怪的事情只是为了让它进入半工作状态:

  • 我不明白save的第一个参数。 如果保存是一个Model静态方法,那对我来说是有意义的,但事实并非如此。 这是一个实例方法,你可以将属性传递给Model构造函数(例如, new X(a:1).save({a:2})是什么意思…?),并且已经可以使用set保存。 所以我无法理解这一点。 我必须通过{}作为占位符来让我指定选项。

  • 有这个forge东西,但我不知道它的目的是什么,因为你已经可以将属性传递给Model构造函数(除非作者发现X.forge({a:1})new X({a:1}) …?)。

  • 我发现我必须显式地指定保存的方法,因为有一个明显的Bookshelf怪癖:Bookshelf基于isNew()方法的select,但isNew()始终为true当您将id传递给模型构造函数在应用程序分配的ID情况下做。 因此,对于应用程序分配的ID,书架将始终执行“插入”,因为它总是认为模型“是新的”。 所以你必须强制该方法“更新”…这只是增加了我的书架混乱。

无论如何,我该如何正确地做到这一点? 我如何使这个插入和更新的工作?

更重要的是,这是在哪里logging? 我真诚地以为它某个地方清楚地logging下来,而我现在看不到树木,所以我真的很欣赏在文档中遵循的一些方向,而不是直接的答案,因为我确实需要解决这个问题。 我在书架上花费了大量的时间而不是实际的开发,所以我几乎希望从一开始就坚持直接使用SQL查询。

这是一个有趣的,花了我一些时间来了解发生了什么事情。

正如您似乎已经发现的,有关patch选项的save()方法文档声明了它

只保存参数中提供的属性以保存。

所以你只需要改变你的代码

 if (model) { model.set('example1', info.example1); return model.save(); } 

set属性将被保存。

但是 但是, 但是

所有的属性都会进入update语句,甚至是id

这是ORM的常见行为,其基本原理是,如果我们从一个事务中获取数据并从另一个事务中保存数据(糟糕的,不好的做法!),则数据可能已被其他客户端更改。 所以只保存部分属性可能会导致状态不一致。

patch属性的存在违反了这个概念。 所以书架可以通过以下改进:

  • 简单地取消patch选项。 (我可以更喜欢)
  • 由于Bookshelf模型跟踪改变的属性,我认为这应该是微不足道的更新更智能这方面。 这个改变也可能导致patch选项的弃用。
  • 另一种方法是将patch语义与更改后的属性相关联,而不仅仅是save()提供的属性。 但不幸的是,这种改变可能会破坏一些用例。
  • 或者最后引入一个新的选项来处理所有更改的属性。 但是那感觉混乱。

在经过几次猜测之后,我似乎已经开始工作了,但是我不知道这是否正确,或者我怎么可能不经过猜测就确定了这一点,我绝对不能坚持它的正确性。

基本上,我能够通过修改“更新”的情况下来工作:

  • 传递属性作为第一个参数save ,而不是设置它们。

通向最终的解决scheme:

 insertOrUpdate: function (info) { return new Radio({'serial':info.serial}).fetch().then(function (model) { if (model) { // pass params to save instead of set() var params = { 'example1' : info.example1 } return model.save(params, { method: 'update', patch: true }) } else { return new Radio({ serial: info.serial, example1: info.example1, example2: info.example2 }).save({}, { method: 'insert' }) } }).then(function (model) { console.log("SUCCESS"); }).catch(function (err) { console.log("ERROR", err); }); } 

我仍然不知道如何/如果forge适合在这里或交易应与第一个参数save在“插入”的情况下。

更重要的是,我现在还不完全确定set是什么。 ORM框架的主要好处之一就是使这种东西透明(即“保存”可以正确地工作,同时让你使用模型而不用考虑它,而且你不需要知道在指向你“保存” – 我应该能够提前未知的任意代码set事情然后能够保存它不知道是什么改变,但它看起来像我不能),所以我不知道我是什么这里实际上是从书架上获得的。 必须有一个更好的方法。