PostgreSQL一对多关系测试:Spring Data JDBC查询返回多行的解决办法
解决Spring Data JDBC一对多查询结果重复问题
方案一:直接用Spring Data JDBC Repository方法(推荐)
既然已经通过@MappedCollection完成一对多关系映射,完全不需要自己编写原生SQL。只需定义父实体的Repository接口:
interface ParentRepository extends CrudRepository<ParentEntity, Long> { Optional<ParentEntity> findByPositionId(Long positionId); }
测试时直接调用parentRepository.findByPositionId(xxx)即可,Spring Data JDBC会自动执行关联查询,并将子表数据聚合到Set<ChildEntity>中,返回单条父实体对象,无需手动处理结果。
方案二:手动聚合原生查询结果
如果必须自定义SQL(比如测试特定SQL逻辑),需要手动将多条查询结果合并为单条带Set的父实体:
- 先修正SQL的别名错误(原SQL中
LEFT JOIN child_table ai却用ct关联,需统一别名):
SELECT pt.*, ct.child_table FROM parent_table pt LEFT JOIN child_table ct ON ct.parent_table_id = pt.id WHERE pt.position_id = ?
- 用JdbcTemplate查询后手动聚合结果:
// 执行查询 List<Map<String, Object>> resultRows = jdbcTemplate.queryForList(sql, positionId); if (resultRows.isEmpty()) { return null; } // 初始化父实体,提取共同的父字段 Long parentId = (Long) resultRows.get(0).get("id"); // 提取其他父字段,比如String otherField = (String) resultRows.get(0).get("other_field"); Set<ChildEntity> childSet = new HashSet<>(); // 遍历所有行,收集子字段到Set中 for (Map<String, Object> row : resultRows) { String childVal = (String) row.get("child_table"); if (childVal != null) { childSet.add(new ChildEntity(childVal)); } } return new ParentEntity(parentId, /* 其他父字段 */ childSet);
(注:如果ParentEntity是不可变的record,需确保构造参数顺序与定义一致)
方案三:用PostgreSQL聚合函数简化SQL
利用PostgreSQL的STRING_AGG函数将子字段合并为单个字符串,再拆分转换为Set:
- 修改SQL(需列出父表所有字段并参与分组):
SELECT pt.id, pt.other_field1, pt.other_field2, STRING_AGG(ct.child_table, ',') AS child_values FROM parent_table pt LEFT JOIN child_table ct ON ct.parent_table_id = pt.id WHERE pt.position_id = ? GROUP BY pt.id, pt.other_field1, pt.other_field2
- 在RowMapper中处理聚合后的字符串:
return jdbcTemplate.queryForObject(sql, new RowMapper<ParentEntity>() { @Override public ParentEntity mapRow(ResultSet rs, int rowNum) throws SQLException { Set<ChildEntity> childSet = new HashSet<>(); String childValues = rs.getString("child_values"); if (childValues != null) { Arrays.stream(childValues.split(",")) .forEach(val -> childSet.add(new ChildEntity(val))); } return new ParentEntity( rs.getLong("id"), rs.getString("other_field1"), rs.getString("other_field2"), childSet ); } }, positionId);
内容的提问来源于stack exchange,提问作者18929371
相关产品推荐
相关产品推荐

