CASE WHEN 在 querydsl 中从列表生成

CASE WHEN in querydsl generate from list

我想为 QueryDsl 中的 SELECT 子句创建一个 NumberExpression,使用 CASE WHEN THEN 构造来检索会话号(业务数据)。 目前,我正在使用这种在 String 中创建 SQL 的方法,由于多种原因,这种方法很糟糕。

    private static StringBuilder getSelectStatement(
        UUID jobInstanceUuid,
        LocalDateTime jobInstanceStartDateTime,
        List<CotCandidate> cotCandidates
    ) {
        StringBuilder selectStatement = new StringBuilder();
        selectStatement.append("SELECT ");
        selectStatement.append(getCaseOfSessionNumber(cotCandidates));
        // skipped other fields for clarity
        selectStatement.append(getFromStatement());
       // skipped where, having, groupBy statements for clarity
       selectStatement.append(" ");

        return selectStatement;
    }

    private static String getCaseOfSessionNumber(List<CotCandidate> cotCandidates) {
        StringBuilder caseSessionNumber = new StringBuilder();
        caseSessionNumber.append("CASE ");
        cotCandidates.forEach(cot -> {
            caseSessionNumber.append(" WHEN (P.SETTLEMENT_SYSTEM = ");
            caseSessionNumber.append(toSqlString(cot.getSettlementSystem()));
            caseSessionNumber.append(" AND P.PAYMENT_MODE = ");
            caseSessionNumber.append(toSqlString(cot.getPaymentMode()));
            caseSessionNumber.append(" ) THEN ");
            caseSessionNumber.append(cot.getSessionNumb());
            caseSessionNumber.append(" ");
        });
        caseSessionNumber.append("END");
        return caseSessionNumber.toString();
    }

我想通过 q​​uerydsl 实现这样的目标:

    void getJobExecutionQuery(List<CotCandidate> cotCandidates) {
        QPaymentOrderEntity order = QPaymentOrderEntity.paymentOrderEntity;
        JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
        List<Tuple> tuple = queryFactory
            .from(order)
            .select(
                getSessionNumb(cotCandidates, order),
            )
            .fetch();
    }

    // This doesn't work cause without otherwise() in the end it return Case.Builder.Cases
    private NumberExpression<Integer> getSessionNumb(List<CotCandidate> cotCandidates, QPaymentOrderEntity order) {
        return cotCandidates.stream()
            .map( cot ->
            Expressions.cases()
                    .when(order.settlementSystem.eq(cot.getSettlementSystem())
                        .and(order.paymentMode.eq(cot.getPaymentMode())))
                    .then(cot.getSessionNumb())
            ).collect(?);
    }

The problem is that I don't know how to build dynamicly case when then : 
To be like that: 

            NumberExpression<Integer> sessionNumber = new CaseBuilder()
            .when(
                order.settlementSystem.eq(cotCandidates.get(0).getSettlementSystem())
                .and(order.paymentMode.eq(cotCandidates.get(0).getPaymentMode())))
            .then(cotCandidates.get(0).getSessionNumb())
            .when(
                order.settlementSystem.eq(cotCandidates.get(n).getSettlementSystem())
                    .and(order.paymentMode.eq(cotCandidates.get(n).getPaymentMode())))
            .then(cotCandidates.get(n).getSessionNumb())
            .otherwise(-1);

问题在于 CASE 语句由三部分组成:

  1. 初始案例 (CaseBuilder)
  2. 中间部分(CaseBuilder.Cases)
  3. 最后一部分 (CaseBuilder.Cases#otherwise(...))

不幸的是,这些构建器类型没有提供通用接口,这基本上使您可以从 window 中选择流畅的 reducer。最初的情况总是需要单独处理:

QPaymentOrderEntity order = QPaymentOrderEntity.paymentOrderEntity;
CaseBuilder caseBuilder = Expressions.cases();
CotCandidate candidate = candidates.get(0);
CaseBuilder.Cases<Integer, NumberExpression<Integer>> intermediateBuilder = caseBuilder.when(order.paymentMode.eq(candidate.getPaymentMode())
        .and(order.settlementSystem.eq(candidate.getSettlementSystem()))).then(candidate.getSessionNumb());

for (int i = 1; i < candidates.size(); i++) {
    candidate = candidates.get(i); 
    intermediateBuilder = intermediateBuilder.when(order.paymentMode.eq(candidate.getPaymentMode())).then(candidate.getSessionNumb());
}

NumberExpression<Integer> finalExpression = intermediateBuilder.otherwise(-1);

循环在技术上是一个 foldLeft 操作。不幸的是,这不能用 Stream 轻松表达,因为 Stream API 仅提供可并行化的 reduce 操作。无法组合两个不同的 CaseWhen 构建器,因此此操作不可并行化。有关 foldLeftreduce 的更详细答案,请参阅 。


旁注:

你这不是在重新发明轮子吗?从 QPaymentOrderEntity 的属性来看,PaymentModeSettlementSystem 似乎是托管类型。仅加入 CotCandidates 并从那里获取 sessionNumb 查询可能会容易得多;

query().from(order).innerJoin(QCotCandidate.cotCandidate)
   .on(QCotCandidate.cotCandidate.settlementSystem.eq(order.settlementSystem)
       .and(QCotCandidate.paymentMode.settlementSystem.eq(order.paymentMode))
   .select(order, QCotCandidate.cotCandidate.sessionNumb)

无论如何,它 CotCandiate 可能不是托管实体。在这种情况下,您将需要一个 VALUES 子句,而 JPQL 默认情况下没有。 (您可能需要为此考虑 blaze-persistence-querydsl 集成)。