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

使用JPA Criteria Builder在一对多关联中查询父及子实体指定字段

解决方案:用Criteria Builder查询仅含指定字段的关联实体集合

要实现查询TransactionStatus集合,且关联的InvoiceTransaction仅包含status和errorMessage字段,推荐使用DTO投影的方式(避免破坏实体约束和原有结构),也可通过修改实体构造函数实现,具体如下:

方式一:使用DTO投影(推荐)

步骤1:创建DTO类

先定义两个DTO类,分别对应父实体和子实体的所需字段:

// 子实体DTO:仅包含需要的字段
@Getter
@Setter
@AllArgsConstructor
public class InvoiceTransactionDTO {
    private String status;
    private String errorMessage;
}

// 父实体DTO:包含自身字段和子实体DTO列表
@Getter
@Setter
@AllArgsConstructor
public class TransactionStatusDTO {
    private String id;
    private String status;
    private Date lastUpdatedDate;
    private Date generatedAt;
    private List<InvoiceTransactionDTO> invoiceTransactions;
}

步骤2:编写Criteria Builder查询代码

通过LEFT JOIN关联子实体,结合构造函数投影和聚合函数收集子实体列表:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
// 声明查询结果为TransactionStatusDTO类型
CriteriaQuery<TransactionStatusDTO> cq = builder.createQuery(TransactionStatusDTO.class);
Root<TransactionStatus> rootTrnStatus = cq.from(TransactionStatus.class);

// LEFT JOIN关联InvoiceTransaction,确保无关联子实体的父实体也能被查询到
Join<TransactionStatus, InvoiceTransaction> invoiceJoin = rootTrnStatus.join("invoiceTransactions", JoinType.LEFT);

// 构造InvoiceTransactionDTO实例,仅提取status和errorMessage字段
Expression<InvoiceTransactionDTO> invoiceDtoExpr = builder.construct(
    InvoiceTransactionDTO.class,
    invoiceJoin.get("status"),
    invoiceJoin.get("errorMessage")
);

// 按父实体的唯一标识字段分组,确保同一父实体的子实体被聚合为列表
cq.groupBy(
    rootTrnStatus.get("id"),
    rootTrnStatus.get("status"),
    rootTrnStatus.get("lastUpdatedDate"),
    rootTrnStatus.get("generatedAt")
);

// 构造TransactionStatusDTO实例,聚合子实体DTO列表
cq.select(builder.construct(
    TransactionStatusDTO.class,
    rootTrnStatus.get("id"),
    rootTrnStatus.get("status"),
    rootTrnStatus.get("lastUpdatedDate"),
    rootTrnStatus.get("generatedAt"),
    builder.collect(invoiceDtoExpr) // 收集分组后的子DTO为列表
));

// 执行查询
List<TransactionStatusDTO> resultList = entityManager.createQuery(cq).getResultList();

方式二:直接使用实体构造函数(不推荐)

若需直接返回原有实体,需先给InvoiceTransaction添加仅含status和errorMessage的构造函数(注意:实体中originalStatus字段为非空约束,构造的实体该字段为null,仅适合用于数据返回,不可后续持久化):

步骤1:修改InvoiceTransaction实体

添加构造函数:

@Entity
@Table(name="invoice_transaction")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class InvoiceTransaction implements Serializable {
    // 原有字段...

    // 添加仅含status和errorMessage的构造函数
    public InvoiceTransaction(String status, String errorMessage) {
        this.status = status;
        this.errorMessage = errorMessage;
        this.originalStatus = null;
        this.generatedAt = null;
    }
}

步骤2:编写Criteria Builder查询代码

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TransactionStatus> cq = builder.createQuery(TransactionStatus.class);
Root<TransactionStatus> rootTrnStatus = cq.from(TransactionStatus.class);
Join<TransactionStatus, InvoiceTransaction> invoiceJoin = rootTrnStatus.join("invoiceTransactions", JoinType.LEFT);

// 构造仅含指定字段的InvoiceTransaction实例
Expression<InvoiceTransaction> invoiceExpr = builder.construct(
    InvoiceTransaction.class,
    invoiceJoin.get("status"),
    invoiceJoin.get("errorMessage")
);

// 按父实体字段分组
cq.groupBy(
    rootTrnStatus.get("id"),
    rootTrnStatus.get("status"),
    rootTrnStatus.get("lastUpdatedDate"),
    rootTrnStatus.get("generatedAt")
);

// 构造TransactionStatus实例,聚合子实体列表
cq.select(builder.construct(
    TransactionStatus.class,
    rootTrnStatus.get("id"),
    rootTrnStatus.get("status"),
    rootTrnStatus.get("lastUpdatedDate"),
    rootTrnStatus.get("generatedAt"),
    builder.collect(invoiceExpr)
));

List<TransactionStatus> resultList = entityManager.createQuery(cq).getResultList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:17:11