使用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
相关产品推荐
相关产品推荐

