我可以使用 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,它基本上是

的一个函数

RootCriteriaQueryCriteriaBuilderPredicate

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