Bookshelf.js - 交易的意外行为

Bookshelf.js - unexpected behaviour with transactions

我已经看了好几天了,我还不能得出任何合理的解释。因此,简要介绍一下背景:我正在使用 bookshelfjs,我想使用事务来执行 3 次插入,并在保存时添加一些额外的引用。为了让事情按照我希望的方式工作,我使用了 bluebird Promise。

router.get('/transaction', function(req, res){

    bookshelf.transaction(function(t){

        return Promise.all([
            //this block is part of transaction!
            new Participant({name: 'Transaction John'}).save(null, {transacting: t}),
            new Participant({name: 'Transaction Doe'}).save(null, {transacting: t}),
            new Town({town_number: 1000, town_title: 'LA'}).save(null, {transacting: t})
        ]).spread(function(p1, p2, t1){

            return Promise.all([ 
                /*
                    this block, however, is not. second address save fails, because I use "p4.get('id')" which does not exists
                    the first address is saved, the second is not; but since I use transactions, nothing should be saved!
                */
                new Address({
                        house_number: 3,
                        street_name: 'Transaction street name',
                        town_id: t1.get('id'),
                        participant_id: p1.get('id')        
                    }).save(null, {transacting: t}),    

                new Address({
                        house_number: 3,
                        street_name: 'Transaction street name 2',
                        town_id: t1.get('id'),
                        participant_id: p4.get('id')        
                    }).save(null, {transacting: t})
            ]);
        });

    }).then(function(){
        res.json('success');        
    }).catch(function(error){
        console.log('error');
        res.json(error);
    });

});

以上代码的查询如下:

{ __cid: '__cid1', sql: 'begin transaction;' }
{ __cid: '__cid1',
  method: 'insert',
  options: undefined,
  bindings: [ 'Transaction John' ],
  sql: 'insert into "participants" ("name") values (?)' }
{ __cid: '__cid1',
  method: 'insert',
  options: undefined,
  bindings: [ 'Transaction Doe' ],
  sql: 'insert into "participants" ("name") values (?)' }
{ __cid: '__cid1',
  method: 'insert',
  options: undefined,
  bindings: [ 1000, 'LA' ],
  sql: 'insert into "towns" ("town_number", "town_title") values (?, ?)' }
[ReferenceError: p4 is not defined]
{ __cid: '__cid1', sql: 'rollback;' }
{ __cid: '__cid1',
  method: 'insert',
  options: undefined,
  bindings: [ 3, 1, 'Transaction street name', 1 ],
  sql: 'insert into "addresses" ("house_number", "participant_id", "street_name"
, "town_id") values (?, ?, ?, ?)' }

从上面的代码可以看出,发生了回滚,和上面的代码是一致的,但是回滚之后,多了一个insert。这不是应该发生的事情。

为什么 Promise.all 在代码的第一部分有效,而在第二部分似乎无效?有错误还是我没有正确理解某些东西?

感谢您的回复!

如 Bookshelf.js 开发者所述:

basically transactions don't play nicely with promise.all

据推测,Bookshelf.js 的新版本正在开发中,并承诺更好地支持交易:https://github.com/tgriesser/knex/pull/786