TypeORM Query Builder多对多关联场景下指定列查询异常问题排查
问题原因分析
你遇到的问题核心在于查询结果的获取方式与期望不匹配,再加上TypeORM实体映射的特性限制:
你用
getOne()来获取结果,但getOne()的作用是返回单个User实体对象,而你期望的是Calendar数组。更关键的是,你的select语句只包含了calendar的字段,没有包含User实体的主键(user.id),TypeORM无法正确构建User实体对象,最终导致返回undefined。你的原生SQL直接返回符合条件的
Calendar行数据,而TypeORM的查询是围绕User实体构建的,两者的返回结构完全不同,这也是结果不符合预期的重要原因。
解决方案
根据你的需求(获取某一用户的所有关联日历),有两种可行的解决方案:
方案1:修正User实体查询,提取关联的Calendars
如果你需要保留User实体的上下文,只需要在select中添加User的主键字段,让TypeORM能正确构建实体,之后从User的calendars属性中提取结果:
// 建议将搜索条件参数化,避免SQL注入风险 const searchTerms = ['IVA', '1636454785616-914385082', 'IVA 3', 'IVA 4']; const whereClauses = searchTerms.map((_, idx) => `calendar.operation LIKE :term${idx}`).join(' OR '); const params = { idUser }; searchTerms.forEach((term, idx) => { params[`term${idx}`] = `%${term}%`; }); const user: User = await this.connection .getRepository(User) .createQueryBuilder('user') .leftJoinAndSelect('user.calendars', 'calendar') .select([ 'user.id', // 必须添加User的主键,否则TypeORM无法构建实体 'calendar.idCalendar', 'calendar.operation', 'calendar.url', 'calendar.path', ]) .where(`user.id = :idUser AND (${whereClauses})`, params) .getOne(); // 提取最终需要的日历数组 const calendars = user?.calendars || [];
方案2:直接查询Calendar表,关联用户
如果你只需要Calendar数组,不需要User实体,更直接的方式是从Calendar的Repository构建查询,通过中间表关联用户:
const searchTerms = ['IVA', '1636454785616-914385082', 'IVA 3', 'IVA 4']; const whereClauses = searchTerms.map((_, idx) => `calendar.operation LIKE :term${idx}`).join(' OR '); const params = { idUser }; searchTerms.forEach((term, idx) => { params[`term${idx}`] = `%${term}%`; }); const calendars: Calendar[] = await this.connection .getRepository(Calendar) .createQueryBuilder('calendar') // 通过中间表关联用户 .innerJoin('users_calendars', 'uc', 'uc.idCalendar = calendar.idCalendar') .innerJoin('user', 'user', 'user.id = uc.idUser') .select([ 'calendar.idCalendar', 'calendar.operation', 'calendar.url', 'calendar.path', ]) .where('user.id = :idUser', { idUser }) .andWhere(`(${whereClauses})`, params) .getMany();
额外提示
- 避免直接拼接SQL字符串作为查询条件,使用参数化查询可以有效防止SQL注入风险。
- 你的原生SQL中使用了
LEFT JOIN,但WHERE条件过滤了calendar的字段,这会自动将连接转换为INNER JOIN(不匹配的行会被过滤掉),所以在TypeORM中使用innerJoin更符合实际逻辑。
内容的提问来源于stack exchange,提问作者Alejandro
相关产品推荐
相关产品推荐

