NestJS中使用TypeORM的save()方法时,如何避免nullable字段被自动覆盖为null?
兄弟,我太懂你这种头疼的处境了!项目里到处都是save(),要全改成update()简直是工程量拉满的噩梦。之前我也碰到过一模一样的问题,摸索出几个实用的办法,你可以根据自己项目的情况选:
方法一:先查询实体再更新(最直观的方式)
直接先从数据库里把要更新的用户查出来,只修改你需要更新的字段,再调用save()。因为这时候你操作的是TypeORM的实体实例,它会追踪字段的变化,只更新你修改过的部分,完全不会碰那些没改的nullable字段:
const user = await userRepository.findOneBy({ id: 1 }); if (user) { user.name = 'Updated Name'; // 这时候save只会更新name字段,profilePicture保持原样 await userRepository.save(user); }
这个方法的好处是逻辑清晰,新手也能一眼看懂,缺点是要多一次查询,但对于大部分业务场景来说,这点性能影响完全可以忽略。
方法二:用preload()合并实体再保存(改动量最小)
TypeORM自带了preload()方法,它能帮你把传入的普通对象和数据库里的实体自动合并——先根据主键查询现有实体,再把你传入的属性覆盖上去,最后返回合并后的实体。这时候再调用save(),就只会更新你传入的字段:
const mergedUser = await userRepository.preload({ id: 1, name: 'Updated Name' }); if (mergedUser) { // 同样,这里只会更新name字段,不会动profilePicture await userRepository.save(mergedUser); }
这个方法特别适合替换你现在直接传普通对象给save()的场景,只需要在原来的save()前面加一行preload就行,改动量极小,几乎不用改业务逻辑,完全能无缝衔接现有代码。
方法三:封装自定义的SafeSave方法(适合大规模项目过渡)
如果项目里save()用得实在太多,不想一个个改,可以自定义一个仓库方法,把preload+save的逻辑封装进去,然后逐步替换原来的save():
// 在你的UserRepository自定义仓库中(如果没用自定义仓库,先创建一个) @EntityRepository(User) export class UserRepository extends Repository<User> { async safeSave(updateData: Partial<User> & { id: number }) { // 先合并传入数据与数据库中的实体 const entity = await this.preload(updateData); if (!entity) { // 这里可以根据业务需求处理,比如抛出错误或者创建新实体 throw new Error('要更新的用户不存在'); } // 调用原生save方法保存合并后的实体 return this.save(entity); } }
之后在业务代码里,就可以慢慢把userRepository.save(...)替换成userRepository.safeSave(...),不用一次性全改,风险小很多,还能逐步完成过渡。
顺便说下背后的原因
当你直接给save()传一个普通JS对象(不是从数据库查出来的实体实例),TypeORM没办法区分哪些字段是你故意要设为null,哪些是你忘了传的。所以它会默认把所有未提供的nullable字段设为null,这是它的默认行为,并没有全局开关能直接关掉。所以核心解决思路就是:让save()操作基于有原始状态的实体实例,而不是裸对象。
内容来源于stack exchange

