Spring JPA OneToMany关联查询如何合并多端属性返回单条DTO记录
实现方案
方案1:JPQL层直接聚合(依赖数据库字符串拼接函数,性能更好)
如果你的项目使用支持分组字符串拼接的数据库,直接修改RandomRecRepository的查询语句,通过分组+拼接函数实现:
@Repository @RequiredArgsConstructor public class RandomRecRepository { private final EntityManager em; public List<RandomRecDto> findRandomApps() { return em.createQuery( // MySQL使用GROUP_CONCAT,PostgreSQL替换为STRING_AGG(g.description, ','),Oracle替换为LISTAGG(g.description, ',') WITHIN GROUP (ORDER BY g.description) "select new study.weba.studyJPA.dto.RandomRecDto(i.id, i.name, GROUP_CONCAT(g.description))" + " from IndieApp i join i.genres g " + " group by i.id, i.name", RandomRecDto.class) .setMaxResults(12) .getResultList(); } }
该方案直接在数据库层完成聚合,执行效率更高,适合数据量较大的场景。
方案2:内存中聚合(不依赖数据库特性,兼容性更好)
如果不想绑定特定数据库,可先查询原始展开数据,再通过Stream流在应用层做分组聚合:
@Repository @RequiredArgsConstructor public class RandomRecRepository { private final EntityManager em; public List<RandomRecDto> findRandomApps() { // 先查询原始的一对多展开数据 List<RandomRecDto> rawList = em.createQuery( "select new study.weba.studyJPA.dto.RandomRecDto(i.id, i.name, g.description)" + " from IndieApp i join i.genres g", RandomRecDto.class) .getResultList(); // 按应用ID分组 Map<Long, List<RandomRecDto>> appGroup = rawList.stream() .collect(Collectors.groupingBy(RandomRecDto::getId)); // 构造合并分类后的DTO List<RandomRecDto> result = new ArrayList<>(); for (Map.Entry<Long, List<RandomRecDto>> entry : appGroup.entrySet()) { List<RandomRecDto> sameAppList = entry.getValue(); String mergedGenres = sameAppList.stream() .map(RandomRecDto::getGenres) .collect(Collectors.joining(", ")); result.add(new RandomRecDto(entry.getKey(), sameAppList.get(0).getName(), mergedGenres)); } // 最后做条数限制 return result.stream().limit(12).collect(Collectors.toList()); } }
该方案兼容性强,不需要修改查询语句,适合数据量较小、需要适配多种数据库的场景。
内容的提问来源于stack exchange,提问作者Lazybear
相关产品推荐
相关产品推荐

