You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Criteria Query实现一对多关联的最新状态查询

用JPA Criteria Query实现最新状态为Pending的交易查询

需求分析

我们要筛选出满足两个核心条件的交易:

  1. 该交易的最新状态是Pending;
  2. 该交易在这个Pending状态之后没有任何其他状态记录(比如Completed、Failed)。

实体类关联定义

先确保实体类正确配置一对多关联:

@Entity
@Table(name = "transaction")
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long credId;
    private Long debtId;
    private BigDecimal amount;
    private BigDecimal charges;

    // 一对多关联交易状态表
    @OneToMany(mappedBy = "transaction", fetch = FetchType.LAZY)
    private List<TransactionStatus> statuses;

    // Getters & Setters
}

@Entity
@Table(name = "transaction_status")
public class TransactionStatus {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String status;
    private LocalDateTime created;

    // 多对一关联交易表
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "transaction_id")
    private Transaction transaction;

    // Getters & Setters
}

Criteria Query 实现代码

下面是完整的查询实现,包含子查询处理核心逻辑:

import jakarta.persistence.criteria.*;
import java.time.LocalDateTime;
import java.util.List;

// 假设这是你的Repository或Service类中的方法
public List<Transaction> findLatestStatusPendingTransactions() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Transaction> cq = cb.createQuery(Transaction.class);
    Root<Transaction> transactionRoot = cq.from(Transaction.class);

    // 子查询1:获取每个交易的最新状态记录的创建时间
    Subquery<LocalDateTime> latestStatusTimeSubquery = cq.subquery(LocalDateTime.class);
    Root<TransactionStatus> latestStatusRoot = latestStatusTimeSubquery.from(TransactionStatus.class);
    latestStatusTimeSubquery.select(cb.greatest(latestStatusRoot.get("created")))
                           .where(cb.equal(latestStatusRoot.get("transaction").get("id"), transactionRoot.get("id")));

    // 子查询2:筛选出那些在最新Pending状态之后还有其他非Pending状态的交易ID(需要排除这些交易)
    Subquery<Long> excludedTxIdsSubquery = cq.subquery(Long.class);
    Root<TransactionStatus> excludedStatusRoot = excludedTxIdsSubquery.from(TransactionStatus.class);
    excludedTxIdsSubquery.select(excludedStatusRoot.get("transaction").get("id"))
                        .where(
                            // 状态创建时间晚于该交易的最新状态时间(也就是后续有新状态)
                            cb.greaterThan(excludedStatusRoot.get("created"), latestStatusTimeSubquery),
                            // 后续状态不是Pending
                            cb.notEqual(excludedStatusRoot.get("status"), "Pending")
                        );

    // 关联交易和状态表,查询最新状态为Pending的记录
    Join<Transaction, TransactionStatus> statusJoin = transactionRoot.join("statuses", JoinType.INNER);
    cq.select(transactionRoot)
      .distinct(true) // 避免一对多关联导致的重复结果
      .where(
          // 当前状态是Pending
          cb.equal(statusJoin.get("status"), "Pending"),
          // 该状态是该交易的最新状态
          cb.equal(statusJoin.get("created"), latestStatusTimeSubquery),
          // 排除那些有后续非Pending状态的交易
          cb.not(transactionRoot.get("id").in(excludedTxIdsSubquery))
      );

    return entityManager.createQuery(cq).getResultList();
}

关键逻辑说明

  • 子查询1:通过cb.greatest()聚合函数,获取每个交易所有状态记录中最新的创建时间,确保我们只关注交易的最后一次状态变更。
  • 子查询2:找出那些在最新状态之后还产生了非Pending状态的交易ID,这些交易不符合需求,需要从结果中排除。
  • 主查询:通过内连接关联交易和状态表,筛选出最新状态为Pending的交易,同时排除掉有后续状态的交易,用distinct(true)避免一对多关联带来的重复数据。

动态查询扩展

如果需要添加动态过滤条件(比如按credId、金额范围筛选),可以直接在主查询中追加条件:

// 示例:动态添加credId过滤
Long targetCredId = 123L;
if (targetCredId != null) {
    Predicate credIdPredicate = cb.equal(transactionRoot.get("credId"), targetCredId);
    cq.where(cb.and(cq.getRestriction(), credIdPredicate));
}

// 示例:动态添加金额范围过滤
BigDecimal minAmount = new BigDecimal("100");
BigDecimal maxAmount = new BigDecimal("1000");
if (minAmount != null && maxAmount != null) {
    Predicate amountRangePredicate = cb.between(transactionRoot.get("amount"), minAmount, maxAmount);
    cq.where(cb.and(cq.getRestriction(), amountRangePredicate));
}

内容的提问来源于stack exchange,提问作者Shyam Sawant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:50:22