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

如何使用JPA在单查询中实现父子表多条件聚合统计?

用JPA实现单查询多聚合统计需求

我来帮你搞定这个JPA多聚合统计的需求!其实不用像原生SQL那样写子查询关联,JPA里有更简洁的实现方式,当然也能适配你原本的子查询思路,下面给你几种可行的方案:

方案一:JPQL条件聚合(推荐,更简洁高效)

首先假设你的实体类关联关系已经正确映射:

@Entity
@Table(name = "Parents")
public class Parent {
    @Id
    private Integer id;
    private String name;
    @OneToMany(mappedBy = "parent")
    private List<Item> items;
    // 构造器、getter、setter省略
}

@Entity
@Table(name = "Items")
public class Item {
    @Id
    private Integer id;
    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
    private Integer fieldA;
    private Integer fieldB;
    // 构造器、getter、setter省略
}

我们可以利用CASE WHEN配合聚合函数,在单查询里直接算出两个统计值,不用子查询:

public interface ParentRepository extends JpaRepository<Parent, Integer> {

    // 返回Object[]的版本
    @Query("SELECT p.id, p.name, " +
           "COUNT(CASE WHEN i.fieldA = 1 THEN 1 END) AS count1, " +
           "COUNT(CASE WHEN i.fieldA = 1 AND i.fieldB = 2 THEN 1 END) AS count2 " +
           "FROM Parent p " +
           "INNER JOIN p.items i " +
           "GROUP BY p.id, p.name")
    List<Object[]> getParentAggregateStats();
}
  • 这里用INNER JOIN和你原SQL逻辑一致,只会返回有符合条件Item的Parent;如果要包含所有Parent(哪怕没有对应Item,统计数为0),换成LEFT JOIN即可。
  • COUNT会自动忽略NULL值,所以满足条件的记录会被计数,不满足的则不会。

用DTO接收结果(更优雅)

如果不想处理Object[],可以定义一个DTO类:

public class ParentStatsDTO {
    private Integer parentId;
    private String parentName;
    private Long count1;
    private Long count2;

    // 注意构造器参数顺序要和查询字段完全匹配
    public ParentStatsDTO(Integer parentId, String parentName, Long count1, Long count2) {
        this.parentId = parentId;
        this.parentName = parentName;
        this.count1 = count1;
        this.count2 = count2;
    }
    // getter方法省略
}

然后修改Repository的查询语句:

@Query("SELECT new com.yourpackage.ParentStatsDTO(p.id, p.name, " +
       "COUNT(CASE WHEN i.fieldA = 1 THEN 1 END), " +
       "COUNT(CASE WHEN i.fieldA = 1 AND i.fieldB = 2 THEN 1 END)) " +
       "FROM Parent p " +
       "INNER JOIN p.items i " +
       "GROUP BY p.id, p.name")
List<ParentStatsDTO> getParentAggregateStats();

方案二:适配你原有的子查询思路

如果你想严格遵循原生SQL的子查询关联逻辑,JPQL也支持子查询写法:

@Query("SELECT p.id, p.name, s1.count1, s2.count2 " +
       "FROM Parent p " +
       "INNER JOIN (SELECT COUNT(i.id) AS count1, i.parent.id AS parentId " +
                  "FROM Item i " +
                  "WHERE i.fieldA = 1 " +
                  "GROUP BY i.parent.id) s1 ON s1.parentId = p.id " +
       "INNER JOIN (SELECT COUNT(i.id) AS count2, i.parent.id AS parentId " +
                  "FROM Item i " +
                  "WHERE i.fieldA = 1 AND i.fieldB = 2 " +
                  "GROUP BY i.parent.id) s2 ON s2.parentId = p.id")
List<Object[]> getParentAggregateStatsWithSubquery();

同样,你也可以用上面的ParentStatsDTO来接收结构化的结果。

方案三:Criteria API实现(适合动态查询场景)

如果后续需要动态调整统计条件,可以用JPA的Criteria API来构建查询:

@Service
public class ParentStatsService {
    @Autowired
    private EntityManager entityManager;

    public List<ParentStatsDTO> getParentStatsWithCriteria() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<ParentStatsDTO> query = cb.createQuery(ParentStatsDTO.class);
        Root<Parent> p = query.from(Parent.class);
        Join<Parent, Item> i = p.join("items", JoinType.INNER);

        // 构建count1的条件统计
        Expression<Long> count1 = cb.count(cb.selectCase()
                .when(cb.equal(i.get("fieldA"), 1), 1)
                .otherwise(null));
        // 构建count2的条件统计
        Expression<Long> count2 = cb.count(cb.selectCase()
                .when(cb.and(cb.equal(i.get("fieldA"), 1), cb.equal(i.get("fieldB"), 2)), 1)
                .otherwise(null));

        query.multiselect(
                p.get("id"),
                p.get("name"),
                count1,
                count2
        ).groupBy(p.get("id"), p.get("name"));

        return entityManager.createQuery(query).getResultList();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:42:39