如何使用JPA Criteria Builder检索一对多关系中的详细信息计数

如何使用JPA Criteria Builder检索一对多关系中的详细信息计数,jpa,Jpa,我有一个一对多的关系,其中一个主实体有两个细节实体。我需要的是为每个主实体获取(计数)相关细节实体的数量 Java实体如下所示: @Entity public class Master { private Long id; private String name; @OneToMany(mappedBy = "masterId", fetch = FetchType.LAZY) private Collection<Detail1> detail1Collection

我有一个一对多的关系,其中一个主实体有两个细节实体。我需要的是为每个主实体获取(计数)相关细节实体的数量

Java实体如下所示:

@Entity
public class Master {
  private Long id;
  private String name;
  @OneToMany(mappedBy = "masterId", fetch = FetchType.LAZY)
  private Collection<Detail1> detail1Collection;
  @OneToMany(mappedBy = "masterId", fetch = FetchType.LAZY)
  private Collection<Detail2> detail2Collection;
  // ..getters / setters
}

@Entity
public class Detail1 {
  private Long id;
  private String name;
  @JoinColumn(name = "MASTER_ID", referencedColumnName = "ID")
  @ManyToOne
  private Master masterId;
  // ..getters / setters
}

@Entity
public class Detail2 {
  private Long id;
  private String name;
  @JoinColumn(name = "MASTER_ID", referencedColumnName = "ID")
  @ManyToOne    
  private Master masterId;
  // ..getters / setters
}
select 
  (select count(*) from detail1 d1 where d1.master_id=master.id) as cntDetail1,
  (select count(*) from detail2 d2 where d2.master_id=master.id) as cntDetail2,
  master.ID,
  master.NAME
from MASTER master 
where master.id=?
JPA准备好的查询看起来像这样,这不是我所表达的,因为count列返回细节计数的笛卡尔乘积

select master0_.ID as col_0_0_, master0_.NAME as col_1_0_, count(detail1col3_.ID) as col_2_0_, count(detail2col4_.ID) as col_3_0_ 
from MASTER master0_ 
left outer join DETAIL1 detail1col3_ on master0_.ID=detail1col3_.MASTER_ID 
left outer join DETAIL2 detail2col4_ on master0_.ID=detail2col4_.MASTER_ID 
where master0_.ID=?
group by master0_.ID, master0_.NAME
以下是我如何使用JPA Criteria API构建查询:

CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Master> master = cq.from(Master.class);
Join<Master, Detail1> detail1 = master.join(Master_.detail1Collection, JoinType.LEFT);
Join<Master, Detail2> detail2 = master.join(Master_.detail1Collection, JoinType.LEFT);
cq.select(cb.tuple(master.get(Master_.id), master.get(Master_.name), cb.count(detail1), cb.count(detail2)));
cq.groupBy(master.get(Master_.id), master.get(Master_.name));
cq.where(cb.equal(master.get(Master_.id), master.getId()));
List<Tuple> list = getEntityManager().createQuery(cq).getResultList();
在getEntityManager()中使用此本机SQL查询。createQuery(sqlString)。getResultList()会导致

java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: (
将JPA查询更改为以下也会导致“意外令牌”异常

public class MasterDetail {
    private long id;
    private String name;
    private long cntDetail1;
    private long cntDetail2;
    // ... getter / setter ...
}

public List<> getMasterDetail(long masterId) {
    final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    final CriteriaQuery<MasterDetail> cq = cb.createQuery(MasterDetail.class);

    final Root<Master> master = cq.from(Master.class);

    final Subquery<Detail1> subDetail1 = cq.subquery(Detail1.class);
    final Root<Detail1> detail1 = subDetail1.from(subDetail1.getResultType());

    final Subquery<Detail2> subDetail2 = cq.subquery(Detail2.class);
    final Root<Detail2> detail2 = subDetail2.from(subDetail2.getResultType());

    cq.multiselect(
            projekt.get(Master_.id),
            projekt.get(Master_.name),
            projekt.get(Projekt_.bauvorhabenOrt),
            cb.count(
                    subDetail1.select(detail1)
                            .where(cb.equal(detail1.get(Detail1_.masterId), master.get(Master_.id))))
                    .alias("cntDetail1"),
            cb.count(
                    subDetail2.select(detail2)
                            .where(cb.equal(detail2.get(Detail2_.masterId), master.get(Master_.id))))
                    .alias("cntDetail2")
    );

    cq.where(cb.equal(master.get(Master_.id), masterId));

    List<MasterDetail> results = getEntityManager().createQuery(cq).getResultList();
    return results;
}
公共类主细节{
私人长id;
私有字符串名称;
私人长途电话1;
私人长途电话2;
//…getter/setter。。。
}
公共列表getMasterDetail(长masterId){
最终CriteriaBuilder cb=entityManager.getCriteriaBuilder();
最终标准查询cq=cb.createQuery(MasterDetail.class);
最终根主节点=cq.from(主节点类);
最终子查询subDetail1=cq.Subquery(Detail1.class);
最终根detail1=subDetail1.from(subDetail1.getResultType());
最终子查询subDetail2=cq.Subquery(Detail2.class);
最终根detail2=subDetail2.from(subDetail2.getResultType());
多重选择(
项目获取(主项目id),
项目get(主项目名称),
项目get(项目bauvorhabenOrt),
计数(
子详细信息1.选择(详细信息1)
.where(cb.equal(detail1.get(detail1.masterId),master.get(master.id)))
.别名(“CNTDail1”),
计数(
子详细信息2.选择(详细信息2)
.where(cb.equal(detail2.get(detail2.masterId),master.get(master.id)))
.别名(“CNT2”)
);
cq.式中(cb.相等(master.get(master.id),masterId));
列表结果=getEntityManager().createQuery(cq).getResultList();
返回结果;
}

任何提示欢迎-谢谢

好的,您应该使用size(),而不是count():@JBNizet:这不是真的,因为size()会导致编译错误:错误:(208,23)java:找不到适合size(javax.persistence.criteria.Subquery)方法javax.persistence.CriteriaBuilder.size(javax.persistence.criteria.Expression)的方法不适用我没有说在现有查询中只需按大小替换count。我说你必须用它。size()返回集合表达式的大小,Master.detail1Collection是一个集合。在我从数据库检索一个MasterDetail对象的情况下,您是对的。在本例中,我可以这样做:Master Master=getEntityManager().find(Master.class,masterId);MasterDetail md=新的MasterDail();md.setCntDetail1(master.getDetail1Collection().size());md.setCntDetail2(master.getDetail2Collection().size());但是,当我为MasterID执行子选择以检索多个实体时,如何处理它呢?好的,您应该使用size(),而不是count():@JBNizet:这不是真的,因为size()会导致编译错误:error:(208,23)java:找不到适合size的方法(javax.persistence.criteria.Subquery)方法javax.persistence.criteria.CriteriaBuilder.size(javax.persistence.criteria.Expression)不适用我并没有说您必须在现有查询中按大小替换count。我说你必须用它。size()返回集合表达式的大小,Master.detail1Collection是一个集合。在我从数据库检索一个MasterDetail对象的情况下,您是对的。在本例中,我可以这样做:Master Master=getEntityManager().find(Master.class,masterId);MasterDetail md=新的MasterDail();md.setCntDetail1(master.getDetail1Collection().size());md.setCntDetail2(master.getDetail2Collection().size());但是,当我为MasterID执行子选择以检索多个实体时,如何处理它呢?
public class MasterDetail {
    private long id;
    private String name;
    private long cntDetail1;
    private long cntDetail2;
    // ... getter / setter ...
}

public List<> getMasterDetail(long masterId) {
    final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    final CriteriaQuery<MasterDetail> cq = cb.createQuery(MasterDetail.class);

    final Root<Master> master = cq.from(Master.class);

    final Subquery<Detail1> subDetail1 = cq.subquery(Detail1.class);
    final Root<Detail1> detail1 = subDetail1.from(subDetail1.getResultType());

    final Subquery<Detail2> subDetail2 = cq.subquery(Detail2.class);
    final Root<Detail2> detail2 = subDetail2.from(subDetail2.getResultType());

    cq.multiselect(
            projekt.get(Master_.id),
            projekt.get(Master_.name),
            projekt.get(Projekt_.bauvorhabenOrt),
            cb.count(
                    subDetail1.select(detail1)
                            .where(cb.equal(detail1.get(Detail1_.masterId), master.get(Master_.id))))
                    .alias("cntDetail1"),
            cb.count(
                    subDetail2.select(detail2)
                            .where(cb.equal(detail2.get(Detail2_.masterId), master.get(Master_.id))))
                    .alias("cntDetail2")
    );

    cq.where(cb.equal(master.get(Master_.id), masterId));

    List<MasterDetail> results = getEntityManager().createQuery(cq).getResultList();
    return results;
}