TypeORM中已弃用findOneById的替代方案:未知主键字段名时的通用数据库查询实现
替代弃用的
findOneById:无需指定主键字段的方案 我完全懂你的痛点——findOneById用起来太顺手了,弃用之后还要手动传主键字段名确实麻烦,尤其是还要兼容多主键场景。其实我们可以利用TypeORM实体的**元数据(Metadata)**来自动获取主键字段,这样就能实现和findOneById一样简洁的写法,不用手动指定主键名。
核心思路:利用Repository的元数据自动获取主键
每个实体的Repository都内置了元数据信息,其中就包含了主键字段的定义(不管是单主键还是多主键)。我们可以通过repo.metadata.primaryColumns拿到所有主键列,然后动态构建查询条件,完美替代findOneById的功能。
完整代码实现
下面是修改后的softDeleteItem函数,支持单主键和多主键场景,无需手动传主键字段名:
async function softDeleteItem( itemId: number | Record<string, any>, // 单主键传数值/字符串,多主键传包含所有主键键值的对象 entityClass: ObjectType<Entity> | EntitySchema<Entity> | string, deletedBy: string ): Promise<boolean> { const repo = db.getRepository(entityClass); const primaryColumns = repo.metadata.primaryColumns; // 动态构建查询条件 const whereCondition: Record<string, any> = {}; if (primaryColumns.length === 1) { // 单主键场景:直接映射主键字段和传入的ID值 whereCondition[primaryColumns[0].propertyName] = itemId; } else { // 多主键场景:校验传入的itemId是否包含所有主键字段 if (typeof itemId !== 'object' || Array.isArray(itemId)) { throw new Error('多主键场景下,itemId必须是包含所有主键字段的对象'); } primaryColumns.forEach(col => { const key = col.propertyName; if (!(key in itemId)) { throw new Error(`缺少必要的主键字段:${key}`); } whereCondition[key] = itemId[key]; }); } // 查询目标条目(找不到会抛出异常,和原findOneById行为一致) const itemToDelete = await repo.findOneByOrFail(whereCondition); // 设置删除人信息 (itemToDelete as ISoftDeletable).deletedBy = deletedBy; await repo.save(itemToDelete); // 执行软删除 await repo.softDelete(whereCondition); return true; }
关键细节说明
- 单主键场景:直接传入ID值(比如
123),函数会自动从元数据中拿到主键字段名(比如id),构建{ id: 123 }的查询条件。 - 多主键场景:需要传入包含所有主键键值的对象,比如
{ userId: 1, orderId: 100 },函数会自动遍历所有主键字段,构建对应的查询条件。 - 版本兼容性:如果你的TypeORM版本低于0.3.x,
findOneByOrFail可能不存在,可以用findOneOrFail({ where: whereCondition })替代。 - 异常处理:
findOneByOrFail会在找不到条目时抛出异常,和原来findOneById的行为一致;如果需要兼容找不到的情况,可以换成findOne并做空值判断。
这个方案完全不需要手动指定主键字段名,写法和findOneById一样简洁,同时完美支持多主键场景,比你之前用QueryBuilder的方案更贴合原来的代码习惯。
内容的提问来源于stack exchange,提问作者Nathan Dixon
相关产品推荐
相关产品推荐

