[英]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.