2017-05-07 51 views
1

我在节点v6.9.5上的Express v4.15.2服务器中使用了Bookshelf v0.10.3和Knex v0.12.9。如何在事务中插入后保存一对多关系

我有一个user表与一个关联的identity表,由identity_user表加入。该表采用Knex创建:

Promise.all([ 
    knex.schema.createTable('user', table => { 
    table.increments('id'); 
    table.string('username'); 
    table.string('password'); 
    table.unique('username'); 
    }), 
    knex.schema.createTable('identity', table => { 
    table.increments('id'); 
    table.string('type'); 
    table.string('value'); 
    }), 
    knex.schema.createTable('identity_user', table => { 
    table.integer('user_id').references('user.id'); 
    table.integer('identity_id').references('identity.id'); 
    table.unique(['user_id', 'identity_id']); 
    }) 
]); 

我的书架模式是这样的:

bookshelf.plugin([ 'registry', 'bookshelf-camelcase' ]); 

const Identity = bookshelf.model('Identity', { 
    tableName: 'identity', 

    user() { 
    return this.belongsTo('User', 'identity_user'); 
    } 
}); 

const User = bookshelf.model('User', { 
    tableName: 'user', 

    initialize() { 
    this.on('saving', model => { 
     if (model.hasChanged('username')) { 
     return model.set('username', String(model.attributes.username).toLowerCase()); 
     } 
    }); 
    this.on('saving', model => { 
     if (model.hasChanged('password')) { 
     return bcrypt.hash(model.attributes.password, 10) 
      .then(hash => model.set('password', hash)); 
     } 
    }); 
    }, 

    identities() { 
    return this.hasMany('Identity', 'identity_user'); 
    } 
}); 

在我createUser功能,我想添加一个新用户,并在一个事务中的身份。

function createUser(req, res, next) { 
    const { email, username, password } = req.body; 

    // some data validation 

    return bookshelf.transaction((t) => { 
    return new User({ username, password }).save(null, { transacting: t }) 
     .then(user => new Identity({ type: 'email', value: email }).save({ userId: user.attributes.id }, { transacting: t })) 
     .then(() => res.sendStatus(204)) 
     .catch(err => { 
     // handle PostgreSQL unique violation error 
     }); 
    }); 
} 

当我运行的服务器,并尝试注册一个新用户,我收到以下错误:

insert into "identity" ("type", "user_id", "value") values ($1, $2, $3) returning "id" - column "user_id" of relation "identity" does not exist { error: insert into "identity" ("type", "user_id", "value") values ($1, $2, $3) returning "id" - column "user_id" of relation "identity" does not exist 

这是PostgreSQL的错误(代码42703 - 未定义列),但一切似乎正确设置。我真的可以用另一双眼睛来看这个。我错过了什么?

在此先感谢!

回答

1

错误是因为书架发现'userId'被插入标识,并推断列名称为'user_id'。但对于连接到M×N的关系,你需要使用attach()代替:

function createUser(req, res, next) { 
    const { email, username, password } = req.body; 

    // some data validation 

    return bookshelf.transaction((t) => { 
    return new User({ username, password }) 
     .save(null, { transacting: t }) 
     .then(user => new Identity({ type: 'email', value: email }) 
     .save(null, { transacting: t })) 
     .then(identity => user.identities() 
     .attach(identity.id, { transacting: t })); 
    }) 
    .then(() => res.sendStatus(204)) 
    .catch(err => { 
    // handle PostgreSQL unique violation error 
    }); 
} 

的变化也似乎为我的工作是直接连接模式(例如.attach(identity, { transacting: t })),但这种方法是不被支持的文件。

编辑我已经在你的模型定义忽略了两个错误,如果hasMany()使用through()电话,要求连接表映射为一个模型只映射到M×N的关系。在上面的例子中,我认为使用简单的'belongsToMany()`调用就足够了。因此,通过替换的关系定义:

user() { 
    return this.belongsToMany('User'); 
} 

identities() { 
    return this.belongsToMany('Identity'); 
} 

EDIT2:移动catch()的交易范围之外,所以异常会自动触发回滚。

编辑3:平坦then()从事务范围中删除链和非db的东西。

+0

感谢您的回复。我试过这个,但得到:“TypeError:user.identities(...)。attach不是一个函数”。更令人困惑的是,用户和身份都被创建,尽管关系不被添加到链接表。我是否必须手动回滚捕获中的更改? –

+0

对不起,我忽略了模型定义中的两个错误。 – flaviodesousa

+0

如果事务范围内没有捕获到异常'bookshelf.transaction(t => {<这里是范围>}),那么事务会自动回滚。但是如果它被抓到(而不是重新抛出),那么提交会自动被书架触发, – flaviodesousa