如何使用JPA Criteria Builder以一对多关系检索详细信息

时间:2016-02-16 07:22:38

标签: jpa

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

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
}

在纯SQL中,查询如下所示:

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();

SQL Query的第二种方法如下所示,但对于没有DETAIL实体的MASTER实体,这会导致NULL值而不是0:

select detail1Count.cntDetail1, detail2Count.cntDetail2, master.ID as ID, master.NAME 
from MASTER master
LEFT JOIN (SELECT master_id, COUNT(*) as cntDetail1 FROM DETAIL1 GROUP BY master_id) detail1Count ON master.id = detail1Count.master_id 
LEFT JOIN (SELECT master_id, COUNT(*) as cntDetail2 FROM DETAIL2 GROUP BY master_id) detail2Count ON master.id = detail2Count.master_id 
where master.id=?

在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;
}

欢迎任何提示 - 谢谢!

0 个答案:

没有答案