如何使用JPA 2.0 Specification实现含关联计数子查询的SQL逻辑
JPA 2.0 Specification 子查询实现方案
你需求对应的完整toPredicate实现代码如下:
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; import org.springframework.data.jpa.domain.Specification; public class CustomT1Specification implements Specification<T1> { @Override public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // 定义返回值为Long类型的子查询 Subquery<Long> countSubQuery = query.subquery(Long.class); // 子查询查询主体为T2实体 Root<T2> t2Root = countSubQuery.from(T2.class); // 子查询逻辑:统计符合条件的T2记录数 countSubQuery.select(criteriaBuilder.count(t2Root)); // 子查询关联条件:T2的T1ObjId和外层T1的ObjId相等 Predicate joinPredicate = criteriaBuilder.equal(t2Root.get("t1ObjId"), root.get("objId")); // 子查询过滤条件:T2的column字段等于指定值 Predicate filterPredicate = criteriaBuilder.equal(t2Root.get("column"), "someText"); countSubQuery.where(criteriaBuilder.and(joinPredicate, filterPredicate)); // 外层查询条件:子查询统计结果等于0 return criteriaBuilder.equal(countSubQuery, 0L); } }
注意事项
- 代码中所有
get()方法的入参为实体类的属性名,需要和你实际定义的T1、T2实体属性名对应,不要填数据库表的列名 - 如果你的T1实体已经配置了和T2的
@OneToMany关联映射,也可以直接基于关联属性构造条件,逻辑和上述写法完全一致
性能优化写法
你原来的SQL逻辑等价于NOT EXISTS判断,后者不需要扫描所有符合条件的T2记录,匹配到第一条就会返回,执行效率更高,对应实现只需要修改子查询的返回和外层判断即可:
// 子查询返回固定值1即可,不需要统计数量 countSubQuery.select(criteriaBuilder.literal(1)); countSubQuery.where(criteriaBuilder.and(joinPredicate, filterPredicate)); // 外层直接判断不存在符合条件的子查询结果 return criteriaBuilder.not(criteriaBuilder.exists(countSubQuery));
内容的提问来源于stack exchange,提问作者chama
相关产品推荐
相关产品推荐

