我想使用NumberExpression
构造为QueryDsl中的SELECT
子句创建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();
}
并且使用querydsl我希望达到这样的水平:
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语句由三部分组成:
CaseBuilder
)CaseBuilder.Cases
)CaseBuilder.Cases#otherwise(...)
)不幸的是,这些构建器类型没有提供通用的界面,基本上不会将流利的减速器的选项扔到窗外。最初的情况总是必须单独处理:
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
构建器组合在一起,因此该操作不可并行化。有关foldLeft
与reduce
的更详细答案,请参见https://stackoverflow.com/a/24316429/2104280。
附带说明:
您不是在这里重新发明轮子吗?从QPaymentOrderEntity
的属性来看,PaymentMode
和SettlementSystem
似乎是托管类型。仅加入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
集成)。