QueryDSL中使用CollectionTable关联属性时,通过Projections.constructor构造DTO包含List<String>类型uri字段报错的解决方案问询
解决QueryDSL Projections.constructor映射@ElementCollection集合字段的「not an entity」错误
我明白你的问题:你用JPA的@ElementCollection维护goose_auth_items和goose_auth_items_uri的关联,想通过QueryDSL的Projections.constructor把查询结果映射到包含List<String> uri的DTO中,但直接传gooseAuthItems.uri会触发「not an entity」错误,去掉uri字段就正常运行。
这个问题的核心是:QueryDSL的ListPath(对应你的gooseAuthItems.uri)是集合类型的路径表达式,不能直接作为Projections.constructor的参数——构造函数需要的是具体的List<String>值,而不是一个关联路径。你得通过显式关联+分组聚合的方式,把多条uri记录合并为一个List,再映射到DTO里。
具体解决方案步骤
1. 确保DTO有对应构造函数
先检查你的GooseAuthGetItemsResponseDto是否包含带List<String> uri参数的构造函数,参数顺序要和后续select里的字段完全匹配:
public class GooseAuthGetItemsResponseDto { private String name; private String userName; private String userPassword; private String folder; private String notes; private List<String> uri = new ArrayList<>(); private LocalDateTime updateDate; // 必须包含这个构造函数 public GooseAuthGetItemsResponseDto(String name, String userName, String userPassword, String folder, String notes, List<String> uri, LocalDateTime updateDate) { this.name = name; this.userName = userName; this.userPassword = userPassword; this.folder = folder; this.notes = notes; this.uri = uri; this.updateDate = updateDate; } // getter/setter方法... }
2. 修改QueryDSL查询代码
你需要显式关联goose_auth_items_uri表,通过groupBy按主实体主键分组,再用Aggregation.list()聚合uri字段为List:
// 获取QueryDSL自动生成的goose_auth_items_uri对应Q类实例 QGooseAuthItemsUri gooseAuthItemsUri = QGooseAuthItemsUri.gooseAuthItemsUri; return queryFactory.select( Projections.constructor(GooseAuthGetItemsResponseDto.class, gooseAuthItems.name, gooseAuthItems.userName, gooseAuthItems.userPassword, gooseAuthItems.folder, gooseAuthItems.notes, Aggregation.list(gooseAuthItemsUri.uri), // 把分组内的uri聚合为List<String> gooseAuthItems.updateDate) ) .from(gooseAuthItems) // 显式关联ElementCollection对应的表 .leftJoin(gooseAuthItems.uri, gooseAuthItemsUri) .where(gooseAuthItems.userMaster.eq(userMaster)) // 按主实体主键+其他非聚合字段分组,保证每个主实体只返回一条记录 .groupBy(gooseAuthItems.itemIdentity, gooseAuthItems.name, gooseAuthItems.userName, gooseAuthItems.userPassword, gooseAuthItems.folder, gooseAuthItems.notes, gooseAuthItems.updateDate) .fetch();
3. 为什么这样能解决问题?
- 显式
leftJoin确保我们能从关联表中正确获取uri数据,避免隐式关联导致的路径解析错误; groupBy按主实体的唯一标识(itemIdentity)和其他非聚合字段分组,保证每个主实体只生成一条DTO记录;Aggregation.list()将分组内的所有uri值收集为一个List<String>,正好匹配DTO构造函数的参数类型,彻底解决「not an entity」的路径映射问题。
额外小技巧
如果你不想显式使用QGooseAuthItemsUri,也可以直接用gooseAuthItems.uri的元素路径来聚合,比如:
Aggregation.list(gooseAuthItems.uri)
但显式关联表的方式逻辑更清晰,也更容易排查后续可能出现的关联问题。
内容的提问来源于stack exchange,提问作者HG_Ko
相关产品推荐
相关产品推荐

