如何用JPA CriteriaBuilder查询无关联ComicMetadataSource的ComicBook记录?
问题分析与解决方案
你的代码存在两个关键问题:
- 错误使用内连接:
comicBook.join("metadata")默认是内连接(INNER JOIN),这会自动过滤掉所有没有关联ComicMetadataSource的ComicBook,后续的isNull条件根本无法匹配到目标数据。 - 未将条件添加到查询中:你创建了
comicBook.get("metadata").isNull()这个判断,但没有把它作为查询条件加入到criteriaQuery的WHERE子句里,相当于这个条件完全没生效。
修正方案一:直接判断关联属性为null
因为你的ComicBook实体已经直接持有metadata引用,最简单的方式是直接判断该属性为null,不需要额外关联表:
final CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaQuery<ComicBook> criteriaQuery = criteriaBuilder.createQuery(ComicBook.class); criteriaQuery.distinct(true); Root<ComicBook> comicBook = criteriaQuery.from(ComicBook.class); if (unscraped) { // 创建"metadata为null"的条件,并添加到WHERE子句 Predicate noMetadata = criteriaBuilder.isNull(comicBook.get("metadata")); criteriaQuery.where(noMetadata); } TypedQuery<ComicBook> query = entityManager.createQuery(criteriaQuery); return query.getResultList();
修正方案二:使用左外连接筛选
如果需要通过关联表的角度查询(比如后续要扩展关联表的其他条件),可以用左外连接(LEFT JOIN),然后判断连接后的ComicMetadataSource为null:
final CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaQuery<ComicBook> criteriaQuery = criteriaBuilder.createQuery(ComicBook.class); criteriaQuery.distinct(true); Root<ComicBook> comicBook = criteriaQuery.from(ComicBook.class); if (unscraped) { // 左外连接metadata,保留所有ComicBook记录 Join<ComicBook, ComicMetadataSource> metadataJoin = comicBook.join("metadata", JoinType.LEFT); // 判断连接后的metadata为null,筛选出未关联的记录 Predicate noMetadata = criteriaBuilder.isNull(metadataJoin); criteriaQuery.where(noMetadata); } TypedQuery<ComicBook> query = entityManager.createQuery(criteriaQuery); return query.getResultList();
补充说明
- 双向
@OneToOne中,ComicBook是关系的被拥有方(mappedBy声明在它的metadata字段),所以直接查询ComicBook的metadata属性是否为null是最直接的方式。 - 注意必须将创建的
Predicate通过criteriaQuery.where()或criteriaQuery.having()添加到查询中,否则条件不会生效。
内容的提问来源于stack exchange,提问作者mcpierce
相关产品推荐
相关产品推荐

