我只需要在一个查询中使用条件api选择主表以及每个状态在另一个表中的出现次数。
我当前的解决方案是在本机查询中运行,但我想以一种基于对象的方式进行查询。 我尝试通过使用特定查询来按条件进行操作,只是选择所有状态,然后手动进行计数。但是使用这种方法,我要调用两个查询:一个在我的主表中获取详细信息,另一个在ID与主表相同的情况下选择所有状态。 有更有效的方法吗?
这是我的本机查询(简体):
SELECT * FROM(
SELECT a.id, a.type, b.count_pending, b.count_failed, b.count_processed
FROM CM AS a
LEFT JOIN ( SELECT
COUNT( CASE WHEN status = 'PENDING' THEN 1 ELSE NULL END ) count_pending,
COUNT( CASE WHEN status = 'FAILED' THEN 1 ELSE NULL END ) count_failed,
COUNT( CASE WHEN status = 'PROCESSED' THEN 1 ELSE NULL END ) count_processed
FROM CM_PARAM WHERE id_cm = :cmId
GROUP BY id_cm
) AS b ON a.id_cm = b.id_cm
WHERE a.id_cm = :cmId) AS a
这是我的CM实体(简体):
@Entity
public class Cm {
@Id
private Long idCm;
private String type;
// other fields
// setters and getters
}
这是我的CM_PARAM实体(简体):
@Entity
public class CmParam {
@Id
private Long idCmp;
@ManyToOne
@JoinColumn(name = "id_cm")
private Cm cm;
private String status;
// other fields
// setters and getters
}
使用本机查询方法,我可以在Cm实体中添加临时字段:
@Transient
private Long countPending;
@Transient
private Long countFailed;
@Transient
private Long countProcessed;
如何使用标准api进行操作,如果可能的话,仅进行一次交易。
预期输出将是这样的:
{
"idCm": 1,
"type": "sms",
"countPending": 5,
"countFailed": 3,
"countProcessed": 9
}
答案 0 :(得分:0)
无需子查询联接就可以重写您的查询:
SELECT
a.id_cm,
a.type
COUNT(CASE WHEN b.status = 'PENDING' THEN 1 ELSE NULL END) countPending,
COUNT(CASE WHEN b.status = 'FAILED' THEN 1 ELSE NULL END) countFailed,
COUNT( CASE WHEN b.status = 'PROCESSED' THEN 1 ELSE NULL END ) countProcessed
FROM CM AS a
LEFT JOIN CM_PARAM AS b ON a.id_cm = b.id_cm
WHERE a.id_cm = ?1
GROUP BY a.id_cm, a.type
您必须将关联的反面添加到Cm
:
@OneToMany(mappedBy = "cm")
private Set<CmParam> params;
(否则,您需要从RIGHT JOIN
到CmParam
的{{1}},这是Hibernate不支持的)
条件查询变为:
Cm
请注意,结果的类型为CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<? extends Object[]> cq = cb.createQuery(new Object[0].getClass());
Root<Cm> a = cq.from(Cm.class);
Join<Cm, CmParam> b = a.join("params", JoinType.LEFT);
cq.where(cb.equal(a.get("idCm"), cb.parameter(Long.class, "idCm")));
cq.groupBy(a.get("idCm"), a.get("type"));
cq.multiselect(
a.get("idCm"),
a.get("type"),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "PENDING"), 1L)
.otherwise(cb.nullLiteral(Long.class))),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "FAILED"), 1L)
.otherwise(cb.nullLiteral(Long.class))),
cb.count(cb.selectCase()
.when(cb.equal(b.get("status"), "PROCESSED"), 1L)
.otherwise(cb.nullLiteral(Long.class))));
。如果要对瞬态字段使用当前方法,最简单的方法是将适当的构造函数添加到Object[]
并使用Cm
方法:
cb.construct()
请注意:
cq.select(cb.construct(Cm.class, a.get("idCm"), a.get("type"), ...))
字段添加到params
,但对Cm
没问题,则可以只使用INNER JOIN
和{{1 }}。 Root<CmParam> b = cq.from(CmParam.class)
中选择的属性不只是Join<CmParam, Cm> a = b.join("cm")
和Cm
,那么您可能还需要在cmId
中列出所有属性