如何使用JPQL查询获取DTO对象,scalar collection element报错如何解决
问题根因
- 你执行
join t.phoneNumbers tp时,tp是phoneNumbers集合中的单个字符串标量值,并非持有phoneNumbers属性的实体对象,你写tp.phoneNumbers属于非法属性引用,直接触发cannot dereference scalar collection element异常。 - 就算你修正上述引用直接传
tp,也无法得到预期结果:join集合后会返回N行结果(N等于该教师的手机号数量),每行仅携带单个手机号,无法直接拼装为携带Set<String>类型手机号集合的DTO实例。
解决方案
方案1:使用Hibernate专属collect()聚合函数构造DTO
Hibernate提供了专属的collect()函数,可以将join后的多行标量值聚合成集合传入DTO构造器,修改你的查询代码如下:
@Override public TeacherDTO fetchTeacherDTOById(Long id) { return entityManager.createQuery("select new service.dto.TeacherDTO(" + "t.id, " + "concat(t.firstName, ' ', t.lastName), " + "collect(tp)) " + "from Teacher t join t.phoneNumbers tp " + "where t.id = :id " + "group by t.id, t.firstName, t.lastName", TeacherDTO.class) .setParameter("id", id) .getSingleResult(); }
注意:
collect()是Hibernate特有的语法,如果你使用其他JPA实现(如EclipseLink)不支持该函数,不要使用该方案。
方案2:先查实体再手动映射为DTO(兼容性最高,推荐)
如果需要兼容所有JPA实现、避免使用厂商专属语法,可以先查询Teacher实体再手动转换为DTO:
@Override public TeacherDTO fetchTeacherDTOById(Long id) { // 关联抓取避免phoneNumbers懒加载问题 Teacher teacher = entityManager.createQuery( "select t from Teacher t left join fetch t.phoneNumbers where t.id = :id", Teacher.class ) .setParameter("id", id) .getSingleResult(); return new TeacherDTO( teacher.getId(), teacher.getFirstName() + " " + teacher.getLastName(), teacher.getPhoneNumbers() ); }
内容的提问来源于stack exchange,提问作者Real Og
相关产品推荐
相关产品推荐

