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

Spring JPA:订单与状态表关联设计及动态查询方案咨询

订单与状态审计表的实体设计及动态查询实现

一、实体类正确设计方式

根据需求场景(单独查询订单/查询订单+最新状态),实体设计需兼顾性能与灵活性,核心是使用懒加载避免不必要的关联查询,同时定义DTO封装前端需要的返回结构。

1. 订单实体(对应FX_ORDER表)

@Entity
@Table(name = "FX_ORDER")
public class Order {
    @Id
    @Column(name = "ORDER_ID")
    private String orderId;
    
    @Column(name = "SEQUENCE")
    private Integer sequence;
    
    @Column(name = "PRODUCT")
    private String product;

    // 懒加载关联状态历史,仅主动调用时才查询STATUS表
    @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
    private List<OrderStatus> statusHistory;

    // 构造器、Getter、Setter省略
}

2. 状态审计实体(对应STATUS表)

@Entity
@Table(name = "STATUS")
public class OrderStatus {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "ORDER_ID")
    private String orderId;
    
    @Column(name = "SEQUENCE")
    private Integer sequence;
    
    @Column(name = "STATUS_CODE")
    private String statusCode;
    
    @Column(name = "LAST_UPDATED_BY")
    private String lastUpdatedBy;
    
    @Column(name = "LAST_UPDATED_DATE_TIME")
    private LocalDateTime lastUpdatedDateTime;

    // 懒加载关联订单,避免循环加载
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
        @JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID", insertable = false, updatable = false),
        @JoinColumn(name = "SEQUENCE", referencedColumnName = "SEQUENCE", insertable = false, updatable = false)
    })
    private Order order;

    // 构造器、Getter、Setter省略
}

3. 返回结果DTO

用于精准返回前端需要的字段结构:

public class OrderWithLatestStatusDTO {
    private String orderId;
    private String product;
    private String status;
    private String lastUpdatedBy;

    // 用于Criteria Builder投影查询的构造器
    public OrderWithLatestStatusDTO(String orderId, String product, String status, String lastUpdatedBy) {
        this.orderId = orderId;
        this.product = product;
        this.status = status;
        this.lastUpdatedBy = lastUpdatedBy;
    }

    // Getter省略
}

关联查询性能说明

使用FetchType.LAZY后,调用Spring Data JPA的findByXXX方法仅查询订单表,不会自动拉取STATUS表数据,只有主动调用order.getStatusHistory()时才会触发关联查询,完全满足“有时仅查询订单详情”的需求。

二、用Criteria Builder实现动态查询获取最新状态记录

以下代码实现了与示例SQL等价的逻辑,同时支持动态添加WHERE子句(比如动态过滤statusCode):

@Repository
public class OrderCustomRepositoryImpl implements OrderCustomRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<OrderWithLatestStatusDTO> findOrdersWithLatestStatus(String statusCode) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<OrderWithLatestStatusDTO> query = cb.createQuery(OrderWithLatestStatusDTO.class);

        // 主查询根:订单表
        Root<Order> orderRoot = query.from(Order.class);

        // 子查询:获取每个(ORDER_ID, SEQUENCE)对应的最新状态更新时间
        Subquery<LocalDateTime> maxDateTimeSubquery = query.subquery(LocalDateTime.class);
        Root<OrderStatus> statusSubRoot = maxDateTimeSubquery.from(OrderStatus.class);
        maxDateTimeSubquery.select(cb.max(statusSubRoot.get("lastUpdatedDateTime")))
                .where(
                        cb.equal(statusSubRoot.get("orderId"), orderRoot.get("orderId")),
                        cb.equal(statusSubRoot.get("sequence"), orderRoot.get("sequence"))
                );

        // 左连接到最新的状态记录,关联条件匹配订单标识+最新时间
        Join<Order, OrderStatus> statusJoin = orderRoot.join("statusHistory", JoinType.LEFT);
        statusJoin.on(
                cb.equal(statusJoin.get("orderId"), orderRoot.get("orderId")),
                cb.equal(statusJoin.get("sequence"), orderRoot.get("sequence")),
                cb.equal(statusJoin.get("lastUpdatedDateTime"), maxDateTimeSubquery)
        );

        // 动态构建WHERE谓词
        List<Predicate> predicates = new ArrayList<>();
        // 示例:动态添加statusCode过滤条件
        if (statusCode != null && !statusCode.isBlank()) {
            predicates.add(cb.equal(statusJoin.get("statusCode"), statusCode));
        }
        // 可添加更多动态条件,比如订单ID范围、产品类型等
        query.where(predicates.toArray(new Predicate[0]));

        // 投影到DTO,直接返回需要的字段
        query.select(cb.construct(
                OrderWithLatestStatusDTO.class,
                orderRoot.get("orderId"),
                orderRoot.get("product"),
                statusJoin.get("statusCode"),
                statusJoin.get("lastUpdatedBy")
        ));

        // 去重避免重复结果
        query.distinct(true);

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

代码逻辑说明

  1. 子查询:确定每个订单(以ORDER_ID+SEQUENCE为唯一标识)的最新状态更新时间,确保只关联最新的一条状态记录。
  2. 动态谓词:通过List<Predicate>可以灵活添加多个查询条件,满足动态WHERE的需求。
  3. 投影查询:直接构造DTO返回,避免加载整个实体,提升查询性能并匹配前端要求的返回结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:46:34