![](/img/trans.png)
[英]Can I use Hibernate's Criteria to generate a subquery in the from clause?
[英]How can i convert a from-subquery into a Hibernate Criteria statement
我有以下三個類的模型。 A類包含1個B,可以包含0個或更多個C. B類和C類都包含我想在A范圍內總結的金額。
class TableA {
@Id
Id id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "table_b_id", nullable = false)
TableB tableB;
@OneToMany(mappedBy = "tableA", fetch = FetchType.LAZY)
Set<TableC> tableCs;
}
class TableB {
@Id
Id id;
@Column(name = "amount_b")
Long amountB;
}
class TableC {
@Id
Id id;
@Column(name = "amount_c", nullable = false)
Long amountC;
@JoinColumn(name = "table_a_id")
TableA tableA;
}
我的任務是計算兩個值“sum(c.amount)+ b.amount”的組成,並查看它們是否在定義的最小/最大間隔內,例如[0,100]。 結果是符合此條件的A列表。 我被要求只在Hibernate Criteria中執行此操作。
為此,我構建了一個表達此要求的SQL語句:
select compositeSum from
(SELECT sum(tableC.amountC) + tableB.amountB as 'compositeSum'
FROM tableA A
left join tableB B on A.b_id = B.id
left join tableC C on C.a_id = A.id) SUBQUERY
where compositeSum between 0 and 100;
由於存在聚合函數“sum()”以及算術運算“+”,我嘗試使用子查詢來解決問題。 我得到的最接近的是以下解決方案:
Long min = 0l;
Long max = 100l;
DetachedCriteria subquery = DetachedCriteria.forClass(TableA.class, "inner")
.createAlias("tableC", "tableC", JoinType.LEFT_OUTER_JOIN)
.createAlias("tableB", "tableB")
.setProjection(Projections.sqlProjection("coalesce(sum(amountC), 0) + amountB", new String[] {"compositeSum"}, new Type[] {StandardBasicTypes.LONG}));
Criteria criteria = session().createCriteria(TableA.class, "outer")
.add(Subqueries.ge(max, subquery))
.add(Subqueries.le(min, subquery));
問題是這會轉換為where子句中使用的子查詢。
select * from A this_
where ? >=
(select sum(amountC) + amountB from table_A tableA_
inner join table_B tableB2_ on tableA_.table_b_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id)
and ? <=
(select sum(amountC) + amountB from table_A tableA_
inner join tableB tableB2_ on tableA_.table_B_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id)
我想構建一個子查詢,可以將其提供給主查詢的from子句。 相反,我有一個子查詢被送到where子句。 這不是我想要的,並導致奇怪的結果。
有誰知道是否有可能將子查詢提供給Hibernate Criteria中的from-clause? 非常感謝!
(更新)
顯然,在內部查詢中添加一個指向外部查詢的附加過濾器可以解決問題。 要從子查詢引用父查詢實體,請使用magic關鍵字“this”。
Long min = 0l;
Long max = 100l;
DetachedCriteria subquery = DetachedCriteria.forClass(TableA.class, "inner")
.createAlias("tableC", "tableC", JoinType.LEFT_OUTER_JOIN)
.createAlias("tableB", "tableB")
.setProjection(Projections.sqlProjection("coalesce(sum(amountC), 0) + amountB", new String[] {"compositeSum"}, new Type[] {StandardBasicTypes.LONG}))
.add(Restrictions.eqProperty("id", "this.id"));
Criteria criteria = session().createCriteria(TableA.class, "outer")
.setProjection(Projections.property("id"))
.add(Subqueries.ge(max, subquery))
.add(Subqueries.le(min, subquery));
添加額外過濾器背后的邏輯是min / max是標量,並且您希望子查詢返回另一個標量而不是列表來進行比較,否則會導致不穩定的行為。
然后它變成一個相關的子查詢:
select * from A this_
where ? >=
(select sum(amountC) + amountB from table_A tableA_
inner join table_B tableB2_ on tableA_.table_b_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id
where tableA_.id = this_.id)
and ? <=
(select sum(amountC) + amountB from table_A tableA_
inner join tableB tableB2_ on tableA_.table_B_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id
where tableA_.id = this_.id)
這個解決方案是一種不同類型的子查詢,並且可能會慢一點,因為您執行子查詢兩次,但它完成了這項工作。
請注意,使用having子句也可以使用SQL解決方案。 在下面,您可以找到上述轉換為having-clause。 唯一的問題是Hibernate Criteria 不支持 having-clauses。
SELECT coalesce(sum(amountC),0) + amountB as 'compositeSum'
FROM table_A tableA
left join table_B tableB on tableA.table_b_id = tableB.id
left join table_C tableC on tableC.table_a_id = tableA.id
group by m.id
having compositeSum between 0 and 100
您不需要子查詢。 簡化您的查詢,如下所示:
SELECT sum(tableC.amountC) + tableB.amountB as 'compositeSum'
FROM tableA A
left join tableB B on A.b_id = B.id
left join tableC C on C.a_id = A.id
where (sum(tableC.amountC) + tableB.amountB) between 0 and 100;
現在改變你的hibernate代碼。
顯然,在內部查詢中添加一個指向外部查詢的附加過濾器可以解決問題。 要從子查詢引用父查詢實體,請使用magic關鍵字“this”。
Long min = 0l;
Long max = 100l;
DetachedCriteria subquery = DetachedCriteria.forClass(TableA.class, "inner")
.createAlias("tableC", "tableC", JoinType.LEFT_OUTER_JOIN)
.createAlias("tableB", "tableB")
.setProjection(Projections.sqlProjection("coalesce(sum(amountC), 0) + amountB", new String[] {"compositeSum"}, new Type[] {StandardBasicTypes.LONG}))
.add(Restrictions.eqProperty("id", "this.id"));
Criteria criteria = session().createCriteria(TableA.class, "outer")
.setProjection(Projections.property("id"))
.add(Subqueries.ge(max, subquery))
.add(Subqueries.le(min, subquery));
添加額外過濾器背后的邏輯是min / max是標量,並且您希望子查詢返回另一個標量而不是列表來進行比較,否則會導致不穩定的行為。
然后它變成一個相關的子查詢:
select * from A this_
where ? >=
(select sum(amountC) + amountB from table_A tableA_
inner join table_B tableB2_ on tableA_.table_b_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id
where tableA_.id = this_.id)
and ? <=
(select sum(amountC) + amountB from table_A tableA_
inner join tableB tableB2_ on tableA_.table_B_id=tableB2_.id
inner join table_C tableC1_ on tableA_.id=tableC1_.table_a_id
where tableA_.id = this_.id)
請注意,使用having子句也可以使用SQL解決方案。 在下面,您可以找到上述轉換為having-clause。 唯一的問題是Hibernate Criteria 不支持 having-clauses。
SELECT coalesce(sum(amountC),0) + amountB as 'compositeSum'
FROM table_A tableA
left join table_B tableB on tableA.table_b_id = tableB.id
left join table_C tableC on tableC.table_a_id = tableA.id
group by m.id
having compositeSum between 0 and 100
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.