我可以使用 JpaSpecificationExecutor 将 where 子句的 in 运算符与 group by same table 的子查询一起使用吗?
Can I make where clause's in operator with subquery with group by same table using JpaSpecificationExecutor?
我正在使用 Spring Data's Repository implment JpaSpecificationExecutor。
我可以使用 JapSpecificationExecutor 的 findAll 方法的规范创建动态 where 子句。
当前代码和 table 类似的模式
历史Table
编号 |留言 |群组编号
1 'text' 1
2 'cotent' 1
3 'ttt' 2
4 'rrr' 3
historyRepository
.findAll(
where(
/** common */
sndId(sndUId))
.and(kind(kind))
.and(sndIdNotNull())
.and(groupTokenNotNull())
/** search option */
.and(searchHistory(historyRequestDto))
/** order by date */
, new PageRequest(0, 50, new Sort(Sort.Direction.DESC, "sndDate"))
)
我想让我的代码生成像那样的原生 mysql 查询
select * from History
where
id IN (
select
min(id)
from
History
where
kind = 4
and kind is not null
and sndId is not null
and groupToken is not null
group by (groupToken)
) order by sndDate desc
limit 0, 10
我可以生成这样的代码吗?
是的,这应该是可能的。
findAll
方法需要一个 Specification
,它基本上是
的一个函数
Root
、CriteriaQuery
、CriteriaBuilder
至 Predicate
https://thoughts-on-java.org/hibernate-tip-subquery-criteriaquery/ 描述如何创建 x in (subquery)
条件:
像这样的东西应该可以工作:
(Root root, CriteriaQuery cq, CriteriaBuilder cb) -> {
Subquery sub = cq.subquery(Long.class);
Root subRoot = sub.from(History.class);
sub.select(cb.min(subRoot.get("id")));
sub.where(/* insert your existing conditions here */);
// check the result of the subquery
return cb.in(sub);
}
我实际上并没有尝试过,所以它肯定需要一些调整,但总体思路应该可行。
像这样的东西应该适合我:
static Specification<CashbackDetail> getCashbacksByStatusSpec(PaymentStatus paymentStatus) {
return (root, query, criteriaBuilder) -> {
// Jointures
Join<CashbackDetail, Payment> paymentJoin = root.join("payment");
Join<Payment, PaymentStatusHistory> historyJoinSet = paymentJoin.joinSet("statusSet");
// Filtre sur le status le plus récent
Subquery<Date> subQuery = query.subquery(Date.class);
Root<PaymentStatusHistory> subFrom = subQuery.from(PaymentStatusHistory.class);
subQuery.select(criteriaBuilder.max(subFrom.get("date")).as(Date.class));
subQuery.where(criteriaBuilder.equal(historyJoinSet.get("entityId"), subFrom.get("entityId")));
// Conditions
Predicate status = criteriaBuilder.equal(historyJoinSet.get("status"), paymentStatus);
Predicate date = criteriaBuilder.equal(historyJoinSet.get("date"), subQuery);
return criteriaBuilder.and(status, date);
};
}
我正在使用 Spring Data's Repository implment JpaSpecificationExecutor。 我可以使用 JapSpecificationExecutor 的 findAll 方法的规范创建动态 where 子句。
当前代码和 table 类似的模式
历史Table
编号 |留言 |群组编号
1 'text' 1
2 'cotent' 1
3 'ttt' 2
4 'rrr' 3
historyRepository
.findAll(
where(
/** common */
sndId(sndUId))
.and(kind(kind))
.and(sndIdNotNull())
.and(groupTokenNotNull())
/** search option */
.and(searchHistory(historyRequestDto))
/** order by date */
, new PageRequest(0, 50, new Sort(Sort.Direction.DESC, "sndDate"))
)
我想让我的代码生成像那样的原生 mysql 查询
select * from History
where
id IN (
select
min(id)
from
History
where
kind = 4
and kind is not null
and sndId is not null
and groupToken is not null
group by (groupToken)
) order by sndDate desc
limit 0, 10
我可以生成这样的代码吗?
是的,这应该是可能的。
findAll
方法需要一个 Specification
,它基本上是
Root
、CriteriaQuery
、CriteriaBuilder
至 Predicate
https://thoughts-on-java.org/hibernate-tip-subquery-criteriaquery/ 描述如何创建 x in (subquery)
条件:
像这样的东西应该可以工作:
(Root root, CriteriaQuery cq, CriteriaBuilder cb) -> {
Subquery sub = cq.subquery(Long.class);
Root subRoot = sub.from(History.class);
sub.select(cb.min(subRoot.get("id")));
sub.where(/* insert your existing conditions here */);
// check the result of the subquery
return cb.in(sub);
}
我实际上并没有尝试过,所以它肯定需要一些调整,但总体思路应该可行。
像这样的东西应该适合我:
static Specification<CashbackDetail> getCashbacksByStatusSpec(PaymentStatus paymentStatus) {
return (root, query, criteriaBuilder) -> {
// Jointures
Join<CashbackDetail, Payment> paymentJoin = root.join("payment");
Join<Payment, PaymentStatusHistory> historyJoinSet = paymentJoin.joinSet("statusSet");
// Filtre sur le status le plus récent
Subquery<Date> subQuery = query.subquery(Date.class);
Root<PaymentStatusHistory> subFrom = subQuery.from(PaymentStatusHistory.class);
subQuery.select(criteriaBuilder.max(subFrom.get("date")).as(Date.class));
subQuery.where(criteriaBuilder.equal(historyJoinSet.get("entityId"), subFrom.get("entityId")));
// Conditions
Predicate status = criteriaBuilder.equal(historyJoinSet.get("status"), paymentStatus);
Predicate date = criteriaBuilder.equal(historyJoinSet.get("date"), subQuery);
return criteriaBuilder.and(status, date);
};
}