Sequelize自定义Setter新增生效但update更新无效果问题求助
问题原因
- Sequelize 原生的字段 getter、setter 仅支持同步逻辑,你当前配置的get、set方法均为异步函数,返回Promise,不符合设计要求。
- 更新操作时,Sequelize 会同步执行脏值检测判断哪些字段需要更新,此时你异步 setter 内的
setDataValue还未执行,Sequelize 认为secret字段没有发生变更,因此不会将该字段加入UPDATE语句,导致更新不生效。新增操作时是全字段写入,碰巧异步逻辑在插入执行前完成了赋值,所以看起来可以正常运行,属于巧合。
解决方案
方案1:将加解密逻辑改为同步实现
如果你的加解密工具支持同步调用,直接去掉get、set方法的async、await修饰符,保证setDataValue同步执行,脏值检测就可以正常识别字段变更,示例如下:
// 公共方法改为同步 decryptGetter(sequelizeModel, key) { try { return module.exports.decryptSync(sequelizeModel.getDataValue(key),config.internal.secret) } catch (err) { throw new Error("GETTER: " + err, __filename); } }, encryptSetter(sequelizeModel, key, value) { try { let encValue = module.exports.encryptSync(value, config.internal.secret); return sequelizeModel.setDataValue(key, encValue); } catch (err) { throw new Error("SETTER: " + err, __filename); } } // 字段定义去掉async修饰 secret: { type: DataTypes.STRING, defaultValue: null, get() { try { return crypt.decryptGetter(this, 'secret'); } catch (err) { throw new Error("Error while decrypting secret: " + err); } }, set(value) { try { return crypt.encryptSetter(this, 'secret', value); } catch (err) { throw new Error("Error while encrypting secret: " + err); } } }
方案2:改用生命周期钩子实现异步加解密
如果加解密必须使用异步逻辑,放弃使用字段getter、setter,改用Sequelize的生命周期钩子实现,钩子本身支持异步操作:
// 模型定义中加入钩子 const Model = sequelize.define('ModelName', { // 其他字段定义 secret: { type: DataTypes.STRING, defaultValue: null } }, { hooks: { beforeCreate: async (instance) => { if (instance.secret) { instance.secret = await crypt.encrypt(instance.secret, config.internal.secret); } }, beforeUpdate: async (instance) => { if (instance.changed('secret')) { instance.secret = await crypt.encrypt(instance.secret, config.internal.secret); } }, afterFind: async (instance) => { if (Array.isArray(instance)) { // 批量查询的兼容处理 await Promise.all(instance.map(async item => { if (item.secret) { item.secret = await crypt.decrypt(item.secret, config.internal.secret); } })) } else { if (instance?.secret) { instance.secret = await crypt.decrypt(instance.secret, config.internal.secret); } } } } })
如果使用静态Model.update()方法执行更新,需要额外添加配置individualHooks: true才能触发beforeUpdate钩子:
await Model.update({ secret: 'newVal', otherField: 'xxx' }, { where: { id: 1 }, individualHooks: true })
内容的提问来源于stack exchange,提问作者db3000
相关产品推荐
相关产品推荐

