如何在Loopback中实现ACID事务

4
我们一直在尝试在Loopback中实现ACID事务,但没有成功。文档中唯一的示例使用“create”方法。我们尝试完全覆盖事件以及多种操作钩子的变化。我们只能使create示例正常工作。
核心要求:我们需要能够在特定模型的create和update方法中启动事务,然后使用Loopback的ORM函数或直接使用SQL更新多个表,并根据业务规则进行提交或回滚。例如,我们需要终端能够接受带有头和详细交易的发票,验证它,更新各种表(如库存和客户),然后将所有更改保存(或回滚)在一个单独的ACID事务中。
使用数据库事务: https://docs.strongloop.com/display/public/LB/Using+database+transactions 这是文档中的示例:
Post.create({title: 't1', content: 'c1'}, {transaction: tx}, function(err, post) {
  post.updateAttributes({content: 'c2', {transaction: tx}, function(err, newPost) {
    //
    newPost.reviews.create({content: 'r1'}, {transaction: tx}, function(err, newPost) {
    });
  }
});

操作钩子:https://docs.strongloop.com/display/public/LB/Operation+hooks

我们只能成功地覆盖了一些核心方法:

    Item.create = function(id, cb){
        console.log('create');  // Success!
    }
    //
    Item.find = function(id, cb){
        console.log('find'); // Success!
    }

    Item.findById = function(id, cb){
        console.log('findById'); // Success!
    }

    Item.getById = function(id, cb){
        console.log('updateAttributes'); // Did not work!
    }

    Item.all = function(id, cb){
        console.log('all'); // Did not work!
    };

    Item.findOrCreate = function(id, test, cb){
        console.log('findOrCreate'); // Did not work!
    }

    Item.replicate = function(id, test, cb){
        console.log('replicate'); // Did not work!
    }

    Item.save = function(id, test, cb){
        console.log('save'); // Did not work!
    }

    Item.updateAll = function(id, test, cb){
        console.log('updateAll'); // Did not work!
    }

    Item.upsert = function(id, test, cb){
        console.log('upsert'); // Did not work!
    }

    Item.updateAttribute = function(id, cb){
        console.log('updateAttribute'); // Did not work!
    };

    Item.updateById = function(id, test, cb){
        console.log('updateById'); // Did not work!
    }

在操作钩子中实现保存、更新等也无法正常工作:
Item.observe('before save', function(ctx, next) {

    console.log('before save');

    ctx.Model.beginTransaction({isolationLevel: ctx.Model.Transaction.READ_COMMITTED}, function(err, tx) {

        // Now we have a transaction (tx)
        console.log('begin transaction', err);

        //tx.commit(function(err) {
        //    console.log('commit', err);
        //})

        //tx.rollback(function(err) {
        //    console.log('rollback', err);
        //})

        next();
    });
})

到目前为止,我们尚未找到任何完整的文档(包括Loopback文档)或任何使用Loopback进行完整ACID事务的完整示例。这个功能是否真正完全发布? - A2MetalCore
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - A2MetalCore
嗨,我只是在评论中添加了LoopBack 2文档的新链接:http://loopback.io/doc/en/lb2/Using-database-transactions.html和http://loopback.io/doc/en/lb2/Operation-hooks.html。 - Stefano Fenu
1个回答

4
我已经成功地实现了一个事务,但我不确定这是否是最好的方法。 我需要检查“instance”,因为当用户未找到时它不会返回错误,而是返回null。我使用内置的 promises 来完成它,因为使用回调函数会非常丑陋。
在下面的示例中,我需要找到两个用户才能更新它们。只有在两个值都被修改时事务才应该成功。如果在此期间发生任何错误,则应停止(回滚)事务。
var firstUserInstance;
var secondUserInstance;

User.beginTransaction('READ COMMITTED', function(err, tx) {
      // find first user and pass it to first 'then'
      User.findById(firstUser.userId, {transaction: tx})
        .then(function(instance){
            if(instance){
                firstUserInstance = instance;
                //Pass second user instance to next 'then'
                return User.findById(firstUser.userId, {transaction: tx});
            }else{
                throw ({message: "User not found", status: 400});
            }
        })
        // Update first user
        .then(function(instance){
            if(instance){
                secondUserInstance = instance;
                //Update first user and pass result to next 'then'
                return firstUserInstance.updateAttribute("attribute", "newValue", {transaction: tx});
            }else{
                throw ({message: "User 'toUserId' not found", status: 400});
            }

        })
        // Update second user
        .then(function(instance){
            if(instance){
                //Update second user and pass result to next 'then'
                return secondUserInstance.updateAttribute("attribute", "newValue", {transaction: tx});
            }else{
                throw ({message: "Error updating", status: 401});
            }
        })
        .then(function(instance){
            if(instance){
                //Everything went OK - commit changes
                console.log(instance);
                tx.commit(function(err){});
            }else{
                throw ({message: "Error updating", status: 401});
            }

        })
        .catch(function(err){
            //Something happened - Any thrown errors will end here - rollback changes
            console.log(err);
            tx.rollback(function(err){});
        });

    });

我对此并不完全满意,我认为还有更好的方法来完成它,但我希望这可以帮到你!


2
在这里,你只对“User”表的提交进行操作。不知道是否有可能创建一个跨越多个表的事务?例如:更新用户、更新配置文件、更新其他内容... - chesscov77
你可以在模型之间传递事务对象。这看起来更加丑陋,但是可行的。Model.findOne({}, { transaction: tx }) - Kevin Alemán

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接