You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的父实体:

  1. 先修正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 = ?
  1. 用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:

  1. 修改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
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:12:40