Loopback的include生成多查询而非单JOIN查询,求解决办法
Loopback 4 一对一关联模型实现单JOIN查询的解决方案
你遇到的问题是Loopback默认处理关联过滤时,会先查询主表,再批量查询关联表(N+1查询),而非生成单条JOIN语句。以下是两种可行的解决办法:
一、自定义Repository方法,手动构建JOIN查询
这是最可靠的方案,通过直接编写SQL或使用查询构建器,将主表和关联表的查询合并为单条语句。
在你的MainRepository中添加自定义查询方法:
import {DefaultCrudRepository, repository, HasOneRepositoryFactory} from '@loopback/repository'; import {Main, MainRelations, Trans} from '../models'; import {PostgresqlDataSource} from '../datasources'; import {inject, Getter} from '@loopback/core'; import {TransRepository} from './trans.repository'; export class MainRepository extends DefaultCrudRepository< Main, typeof Main.prototype.id, MainRelations > { public readonly trans: HasOneRepositoryFactory<Trans, typeof Main.prototype.id>; constructor( @inject('datasources.postgresql') dataSource: PostgresqlDataSource, @repository.getter('TransRepository') protected transRepositoryGetter: Getter<TransRepository>, ) { super(Main, dataSource); this.trans = this.createHasOneRepositoryFactoryFor('trans', transRepositoryGetter); this.registerInclusionResolver('trans', this.trans.inclusionResolver); } // 自定义方法:合并主表与关联表的查询和过滤 async findWithTransFilter(filterParams: { effectiveDateLte: string; expiryDateGte: string; segmentLike: string; }) { // 使用Loopback数据源的查询构建器创建JOIN查询 const query = this.dataSource.createQueryBuilder() .select([ 'main.id', 'main.code', 'main.grade', 'main.ebmethod', 'main.cdnper', 'main.usper', 'main.mandatory', 'main.effectivedate', 'main.expirydate', 'trans.id AS trans_id', 'trans.segment', 'trans.subsegment', 'trans.cdnratebasis', 'trans.usratebasis' ]) .from('main', 'main') .innerJoin('trans', 'trans', 'main.id = trans.mainid') .where('main.effectivedate <= :effectiveDate', {effectiveDate: filterParams.effectiveDateLte}) .andWhere('main.expirydate >= :expiryDate', {expiryDate: filterParams.expiryDateGte}) .andWhere('trans.segment ilike :segment', {segment: `%${filterParams.segmentLike}%`}) .orderBy('main.code', 'ASC'); // 执行查询并转换为模型实例 const rawResults = await query.execute(); return rawResults.map(row => { const main = new Main({ id: row.id, code: row.code, grade: row.grade, ebMethod: row.ebmethod, cdnPer: row.cdnper, usPer: row.usper, mandatory: row.mandatory, effectiveDate: row.effectivedate, expiryDate: row.expirydate, }); main.trans = new Trans({ id: row.trans_id, segment: row.segment, subsegment: row.subsegment, cdnRateBasis: row.cdnratebasis, usRateBasis: row.usratebasis, mainId: row.id, }); return main; }); } }
之后在控制器中调用这个方法即可获取合并查询的结果。
二、调整过滤条件写法,利用Loopback的关联属性过滤
尝试将关联表的过滤条件直接放到主查询的where中,而非include的scope里,部分Loopback版本的PostgreSQL连接器会自动生成JOIN语句:
{ "where": { "and": [ {"effectiveDate": {"lte": "2023-03-01"}}, {"expiryDate": {"gte": "2023-03-01"}}, {"trans.segment": {"ilike": "%Ware%"}} ] }, "order": "code asc", "include": ["trans"] }
这种方式更简洁,但兼容性取决于你使用的Loopback版本和数据库连接器,如果仍生成多查询,建议使用第一种自定义方法。
原因说明
Loopback默认的include+scope机制为了兼容复杂关联和多数据库场景,采用了"先查主表,再批量查关联表"的策略,这会导致N+1查询。而自定义JOIN查询可以直接合并两个表的逻辑,减少数据库交互次数,提升性能。
内容的提问来源于stack exchange,提问作者Niv
相关产品推荐
相关产品推荐

