Sequelize自定义BinToUUID数据类型创建记录时外键被覆盖如何解决
问题核心原因
1. _sanitize方法逻辑错误
你当前的_sanitize方法只要传入值不是Buffer类型,就直接生成新的UUID,完全覆盖了create方法传入的合法字符串UUID外键值,这是外键不匹配的核心原因。
2. 关联配置方向错误
你当前配置的是Room.hasOne(User, {foreignKey: 'roomId'}),这个逻辑是外键roomId存放在user表中,和你在room表中定义userId作为外键关联user表的结构不符,关联方向写反了。
修复步骤
1. 修正BinToUUID类的_sanitize方法
修改逻辑:只有当值为空时才生成新UUID,合法的字符串UUID直接返回,Buffer类型的正常转成UUID字符串即可。
_sanitize(value) { // 如果已经是合法的UUID字符串,直接返回 if (typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) { return value; } // 如果是Buffer类型,转成UUID字符串 if (value instanceof Buffer) { const str = value.toString('hex'); return [ str.slice(0, 8), str.slice(8, 12), str.slice(12, 16), str.slice(16, 20), str.slice(20, 32), ].join('-'); } // 只有值为空的时候才生成新的UUID if (!value) { return uuidv1(); } // 其他情况返回原值 return value; }
可选优化:修正_bindParam方法的语法错误,options.escape已经会自动给字符串加引号,不需要手动额外加单引号:
_bindParam(value, options) { return `UUID_TO_BIN(${options.escape(value)})`; }
2. 修正模型的默认值配置
你当前把defaultValue设为Sequelize.BinToUUID是错误的,不需要显式设置,_sanitize方法里的空值生成逻辑已经覆盖了默认值的需求,或者如果要显式设置可以写成:
defaultValue: () => uuidv1()
3. 修正关联配置
按照你的表结构,room表存userId、groupId作为外键,关联应该用belongsTo而不是hasOne:
// Room属于一个User,外键userId存在room表 db.Room.belongsTo(db.User, { foreignKey: 'userId', targetKey: 'userId' }); // Room属于一个Group,外键groupId存在room表 db.Room.belongsTo(db.Group, { foreignKey: 'groupId', targetKey: 'groupId' }); // 如果需要User关联多个Room的话可以再加这个 db.User.hasMany(db.Room, { foreignKey: 'userId' }); db.Group.hasMany(db.Room, { foreignKey: 'groupId' });
4. 调整create调用参数
如果是传已经存在的userId、groupId,不需要include关联,直接传值即可:
Room.create({ name: '测试房间', userId: '你已经存在的用户UUID', groupId: '你已经存在的群组UUID' })
如果是要同时创建关联的User、Group数据,才需要加include配置,此时不需要手动传userId、groupId,Sequelize会自动回填。
内容的提问来源于stack exchange,提问作者user1790300
相关产品推荐
相关产品推荐

