Knex.js 多个链接查询

Knex.js multiple chained queries

我目前正在使用 express 开发一个项目,我正在使用 knex.js 来处理迁移和查询。

我仍在努力掌握承诺的概念以及如何使用 knex 运行 进行多个查询。

我有以下代码将新记录插入我的数据库,它位于我的 Unit model 文件中。

this.addUnit = function(unit_prefixV, unit_nameV, unit_descriptionV, profile_id) {
         return new Promise(function(resolve, reject) {
             knex.insert({ unit_prefix: unit_prefixV, unit_name: unit_nameV, unit_description: unit_descriptionV })
                .into('units').then(function(unit) {
                    resolve(unit)
                }).catch(function(error) {
                    reject(error)
                })
         })
    }

在我的 routes.js 文件中,然后我根据 post 请求调用它,如下所示:

app.post('/dashboard/unit/add', ensureAuthenticated, function(req, res) {
        let postErrors = []
        if (req.body.unit_name.trim() == "") {
            postErrors.push('Unit name cannot be empty.')
        }

        if (req.body.unit_prefix.trim() == "") {
            postErrors.push('Unit prefix cannot be empty.')
        }

        if (req.body.unit_description.trim() == "") {
            postErrors.push('Unit description cannot be empty.')
        }

        if (postErrors.length > 0) {
            res.render('addUnit', { errors: postErrors, user: req.user })
        } else {
            unitModel.addUnit(req.body.unit_prefix.trim(), req.body.unit_name.trim(), req.body.unit_description.trim(), req.session.passport.user.id).then(function(unit) {
                res.redirect('/dashboard')
            })
        }
    })

这成功地将一条新记录插入到我的 units table,但是,我想 select 来自用户 table 的用户 ID 与匹配的 profile_id然后再插入一条记录到我的users_unitstable。全部在 this.addUnit 函数内。

作为参考,我的 users table 包括:

我的 users_units table 包括:

我已经尝试链接查询,但它只执行初始插入查询而不执行其他查询。这是相当丑陋的尝试:

this.addUnit = function(unit_prefixV, unit_nameV, unit_descriptionV, profile_id) {
         return new Promise(function(resolve, reject) {
             knex.insert({ unit_prefix: unit_prefixV, unit_name: unit_nameV, unit_description: unit_descriptionV })
                .into('units').then(function(unit) {
                    knex('users').where({ "google_id": profile_id }).select('id').then(function(uid) {
                        knex.insert({ user_id: uid, unit_id: unit }).into('users_units').then(function(user_units) {
                            resolve(user_unit)
                        }).catch(function(error) {
                            reject(error)
                        })
                        resolve(uid)
                    })
                    console.log(unit)
                    resolve(unit)
                }).catch(function(error) {
                    reject(error)
                })
         })
    }

任何帮助将不胜感激!

你快到了。有几个简单的要点可以掌握:

  • 一个 Promise 只能被 reolved 一次
  • 无论如何都不需要显式 Promise,因为可以返回自然发生的 Promise
  • return 每个阶段的 Promise ...
  • ...一直到最里面,返回值就是最终传递的结果
  • 除非您想注入自己的自定义错误消息或采取补救措施,否则无需明确处理错误。

考虑到所有这些,您可以这样写:

this.addUnit = function(unit_prefixV, unit_nameV, unit_descriptionV, profile_id) {
    return knex.insert({ 'unit_prefix':unit_prefixV, 'unit_name':unit_nameV, 'unit_description':unit_descriptionV }).into('units')
 // ^^^^^^
    .then(function(unit) {
        return knex('users').where({ 'google_id':profile_id }).select('id')
     // ^^^^^^
        .then(function(uid) {
            return knex.insert({ 'unit_id':unit, 'user_id':uid }).into('users_units')
         // ^^^^^^
            .then(function(user_units) {
                return { 'unit_id':unit, 'user_id':uid, 'user_units':user_units };
             // ^^^^^^
            });
        });
    });
}

如果调用者只对进程的success/failure感兴趣而不是完整的{ unit, uid, user_units }对象,那么最里面的.then()可以省略:

this.addUnit = function(unit_prefixV, unit_nameV, unit_descriptionV, profile_id) {
    return knex.insert({ 'unit_prefix':unit_prefixV, 'unit_name':unit_nameV, 'unit_description':unit_descriptionV }).into('units')
    .then(function(unit) {
        return knex('users').where({ 'google_id':profile_id }).select('id')
        .then(function(uid) {
            return knex.insert({ 'unit_id':unit, 'user_id':uid }).into('users_units');
        });
    });
}

.addUnit() 返回的承诺仍将交付 user_units,调用者可以使用或忽略它。

这些解决方案(以及其他解决方案)有一​​个主要的附带条件;像这样的 multi-stage 更新查询真的应该包含在 transaction - ie something that allows earlier stages to be rolled back. Otherwise a failure part way through is likely to leave the database in some indeterminate state. This answer 中,这是一个很好的起点。