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

如何在JPA原生查询中获取含列表属性的对象集合

JPA原生查询返回带列表属性的对象解决方案

由于JPA原生查询返回的是扁平化结果集,无法直接通过接口投影自动映射带集合属性的对象,以下提供两种可行的实现方案:

方案一:Repository查询原始数据,服务层分组转换

步骤1:定义中间投影接口

用于接收关联查询的扁平化结果:

public interface ExampleChildProjection {
    Integer getExampleId();
    String getExampleLabel();
    Integer getChildId();
    String getChildCode();
    String getChildLabel();
}

步骤2:编写Repository原生查询方法

关联两张表查询所有需要的字段:

@Query(nativeQuery = true, value = """
    SELECT e.id AS example_id, e.label AS example_label,
           c.id AS child_id, c.code AS child_code, c.label AS child_label
    FROM example e
    LEFT JOIN child c ON e.id = c.example_id
    ORDER BY e.id
""")
List<ExampleChildProjection> searchRaw();

步骤3:服务层分组转换为目标接口

将扁平化结果按Example ID分组,组装成带Child列表的ExampleInterface实例:

public List<ExampleInterface> search() {
    List<ExampleChildProjection> rawResults = exampleRepository.searchRaw();
    Map<Integer, ExampleInterface> exampleMap = new HashMap<>();

    for (ExampleChildProjection projection : rawResults) {
        Integer exampleId = projection.getExampleId();
        // 不存在则创建新的ExampleInterface实现
        ExampleInterface example = exampleMap.computeIfAbsent(exampleId, id -> new ExampleInterface() {
            private final Integer idVal = id;
            private final String labelVal = projection.getExampleLabel();
            private final List<ChildInterface> children = new ArrayList<>();

            @Override
            public Integer getId() {
                return idVal;
            }

            @Override
            public String getLabel() {
                return labelVal;
            }

            @Override
            public List<ChildInterface> getChildren() {
                return children;
            }
        });

        // 若存在Child数据,添加到对应Example的子列表中
        if (projection.getChildId() != null) {
            ChildInterface child = new ChildInterface() {
                @Override
                public Integer getId() {
                    return projection.getChildId();
                }

                @Override
                public String getCode() {
                    return projection.getChildCode();
                }

                @Override
                public String getLabel() {
                    return projection.getChildLabel();
                }
            };
            example.getChildren().add(child);
        }
    }

    return new ArrayList<>(exampleMap.values());
}

方案二:使用Hibernate ResultTransformer直接转换

该方案依赖Hibernate API,可在Repository层完成结果转换:

import org.hibernate.transform.ResultTransformer;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

// 在Repository类中注入EntityManager
@PersistenceContext
private EntityManager entityManager;

public List<ExampleInterface> search() {
    String sql = """
        SELECT e.id AS example_id, e.label AS example_label,
               c.id AS child_id, c.code AS child_code, c.label AS child_label
        FROM example e
        LEFT JOIN child c ON e.id = c.example_id
        ORDER BY e.id
    """;

    return entityManager.createNativeQuery(sql)
            .unwrap(org.hibernate.query.Query.class)
            .setResultTransformer(new ResultTransformer() {
                private Map<Integer, ExampleInterface> exampleMap = new HashMap<>();

                @Override
                public Object transformTuple(Object[] tuple, String[] aliases) {
                    Integer exampleId = (Integer) tuple[0];
                    String exampleLabel = (String) tuple[1];
                    Integer childId = (Integer) tuple[2];
                    String childCode = (String) tuple[3];
                    String childLabel = (String) tuple[4];

                    ExampleInterface example = exampleMap.computeIfAbsent(exampleId, id -> new ExampleInterface() {
                        private final Integer idVal = id;
                        private final String labelVal = exampleLabel;
                        private final List<ChildInterface> children = new ArrayList<>();

                        @Override
                        public Integer getId() {
                            return idVal;
                        }

                        @Override
                        public String getLabel() {
                            return labelVal;
                        }

                        @Override
                        public List<ChildInterface> getChildren() {
                            return children;
                        }
                    });

                    if (childId != null) {
                        ChildInterface child = new ChildInterface() {
                            @Override
                            public Integer getId() {
                                return childId;
                            }

                            @Override
                            public String getCode() {
                                return childCode;
                            }

                            @Override
                            public String getLabel() {
                                return childLabel;
                            }
                        };
                        example.getChildren().add(child);
                    }

                    return example;
                }

                @Override
                public List<?> transformList(List collection) {
                    return new ArrayList<>(exampleMap.values());
                }
            })
            .getResultList();
}

关键注意事项

  • 必须添加ORDER BY e.id,确保同一Example的所有Child数据连续返回,避免分组时遗漏数据。
  • 方案一基于JPA规范实现,兼容性更好;方案二依赖Hibernate特定API,代码更紧凑。

内容的提问来源于stack exchange,提问作者Pro Grammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:22:45