插入事务后如何保存一对多关系
How to save one-to-many relationship after insert in a transaction
我在节点 v6.9.5 上的 Express v4.15.2 服务器中使用 Bookshelf v0.10.3 和 Knex v0.12.9。
我有一个 user
table 和一个关联的 identity
table,由一个 identity_user
table 加入。 table 是使用 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 (, , ) returning "id" - column "user_id" of relation "identity" does not exist { error: insert into "identity" ("type", "user_id", "value") values (, , ) returning "id" - column "user_id" of relation "identity" does not exist
这是一个 PostgreSQL 错误(代码 42703 - 未定义的列),但一切似乎都已正确设置。我真的可以用另一双眼睛来看待这个。我错过了什么?
提前致谢!
错误是因为 Bookshelf 发现 'userId' 被插入到 Identity 中并推断列名称为 'user_id'。但是要连接到 MxN 关系,您需要改用 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 })
),但文档不支持这种方法。
编辑 我忽略了您的模型定义中的两个错误,如果使用 through()
调用,hasMany()
仅映射到 MxN 关系,这需要要映射为模型的连接 table。在上述情况下,我认为使用简单的“belongsToMany()”调用就足够了。因此,将您的关系定义替换为:
user() {
return this.belongsToMany('User');
}
和
identities() {
return this.belongsToMany('Identity');
}
Edit2: 将catch()
移动到事务范围之外,所以异常会自动触发回滚。
Edit3:奉承 then()
链和非数据库内容已从事务范围中删除。
我在节点 v6.9.5 上的 Express v4.15.2 服务器中使用 Bookshelf v0.10.3 和 Knex v0.12.9。
我有一个 user
table 和一个关联的 identity
table,由一个 identity_user
table 加入。 table 是使用 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 (, , ) returning "id" - column "user_id" of relation "identity" does not exist { error: insert into "identity" ("type", "user_id", "value") values (, , ) returning "id" - column "user_id" of relation "identity" does not exist
这是一个 PostgreSQL 错误(代码 42703 - 未定义的列),但一切似乎都已正确设置。我真的可以用另一双眼睛来看待这个。我错过了什么?
提前致谢!
错误是因为 Bookshelf 发现 'userId' 被插入到 Identity 中并推断列名称为 'user_id'。但是要连接到 MxN 关系,您需要改用 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 })
),但文档不支持这种方法。
编辑 我忽略了您的模型定义中的两个错误,如果使用 through()
调用,hasMany()
仅映射到 MxN 关系,这需要要映射为模型的连接 table。在上述情况下,我认为使用简单的“belongsToMany()”调用就足够了。因此,将您的关系定义替换为:
user() {
return this.belongsToMany('User');
}
和
identities() {
return this.belongsToMany('Identity');
}
Edit2: 将catch()
移动到事务范围之外,所以异常会自动触发回滚。
Edit3:奉承 then()
链和非数据库内容已从事务范围中删除。