sequelize-typescript中$set无法设置HasMany关联的解决问询
解决Sequelize-Typescript中HasMany关联的批量替换问题
问题场景
现有两个Sequelize-Typescript模型:
@Table export class Role extends Model { @PrimaryKey @Column({type: DataType.UUID, defaultValue: DataType.UUIDV4}) declare id: string @Column name: string @Column account_id: string @CreatedAt created_at: Date @UpdatedAt updated_at: Date @HasMany(() => RolePermission, {foreignKey: 'role_id'}) permissions: RolePermission[] } @Table({tableName: 'role_permission'}) export class RolePermission extends Model { @PrimaryKey @Column({type: DataType.UUID, defaultValue: DataType.UUIDV4}) declare id: string @Column permission: string @CreatedAt created_at: Date @UpdatedAt updated_at: Date @BelongsTo(() => Role, {foreignKey: 'role_id'}) role: Role }
创建角色时附带权限的逻辑正常:
const form = { name: 'role name here', permissions: ['home.view'] } const role = await Role.create({ account_id: request.account, name: form.name, permissions: form.permissions.map(permission => ({permission})) }, { include:[RolePermission] })
但更新角色权限时,以下代码无法替换旧权限(仅更新名称成功,旧权限保留):
const form = { name: 'role name here', permissions: ['home.view'] } const role = await Role.findByPk(id) if(!role){ throw new Error("role not found") } await role.update({ name: form.name, }) await role.$set('permissions', form.permissions.map(permission => RolePermission.build({permission})))
尝试清除旧权限的两种方式均失败:
await role.$set('permissions', [])报SQL死锁错误role.permissions = []; await role.save()无任何效果
可行解决方案
方案一:事务包裹 + 先删后创(最稳定)
通过事务确保删除旧权限和创建新权限的原子性,避免死锁和数据不一致:
const form = { name: 'role name here', permissions: ['home.view'] } const role = await Role.findByPk(id) if(!role){ throw new Error("role not found") } // 开启数据库事务 const transaction = await sequelize.transaction() try { // 更新角色名称 await role.update({ name: form.name }, { transaction }) // 删除当前角色的所有旧权限 await RolePermission.destroy({ where: { role_id: role.id }, transaction }) // 批量创建新权限(若权限列表非空) if (form.permissions.length > 0) { await RolePermission.bulkCreate( form.permissions.map(perm => ({ permission: perm, role_id: role.id })), { transaction } ) } // 提交事务 await transaction.commit() } catch (error) { // 异常时回滚事务 await transaction.rollback() throw error }
方案二:正确使用$set关联方法
确保关联数据被正确加载,或直接传入符合关联要求的对象数组:
const form = { name: 'role name here', permissions: ['home.view'] } // 加载角色时同时关联权限数据 const role = await Role.findByPk(id, { include: [RolePermission] }) if(!role){ throw new Error("role not found") } await role.update({ name: form.name }) // 直接传入权限对象数组,由Sequelize自动处理关联 const newPermissions = form.permissions.map(perm => ({ permission: perm })) await role.$set('permissions', newPermissions)
失败原因说明
$set('permissions', [])报死锁:未在事务中执行,并发场景下数据库锁冲突导致,用事务包裹即可解决。role.permissions = []; await role.save()无效:HasMany关联的直接赋值不会自动同步到数据库,必须使用$set、$add等Sequelize提供的关联操作方法。
内容的提问来源于stack exchange,提问作者Ionel Lupu
相关产品推荐
相关产品推荐

