如何通过Spring Data JPA Specification按列实现Distinct查询?
解决Spring Data JPA Specification实现PostgreSQL DISTINCT ON查询的问题
问题原因分析
你之前尝试的两种方法都没达到预期,核心是没搞懂普通DISTINCT和PostgreSQL特有的DISTINCT ON的区别:
query.distinct(true)是JPA标准的全局去重,只有当查询返回的所有字段完全相同时才会去重,你的两条记录title和created_at不同,所以会全部返回;- 第二种方法只选择了
employee_id列,虽然能去重,但只返回ID,不是你需要的整条合同记录。
正确实现方式
PostgreSQL的DISTINCT ON需要指定去重列,并配合排序确保每组的第一条是最新记录。Spring Data JPA的标准Specification API不直接支持这个语法,但可以通过Hibernate的扩展API实现:
import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.springframework.data.jpa.domain.Specification; public class ContractSpecifications { public static Specification<Contract> latestByEmployeeId() { return (root, query, criteriaBuilder) -> { // 转换为Hibernate的CriteriaBuilder,支持DISTINCT ON语法 HibernateCriteriaBuilder hibernateCb = (HibernateCriteriaBuilder) criteriaBuilder; // 指定按employee_id列去重 query.distinctOn(hibernateCb, root.get("employeeId")); // 设置排序规则:先按employee_id分组,再按created_at降序,确保最新记录排在每组第一位 query.orderBy( criteriaBuilder.asc(root.get("employeeId")), criteriaBuilder.desc(root.get("createdAt")) ); return null; // 无额外过滤条件,返回null即可 }; } }
代码说明
- 依赖Hibernate扩展:因为JPA标准API没有
distinctOn方法,需要借助Hibernate的HibernateCriteriaBuilder来生成PostgreSQL特有的DISTINCT ON语法; - 排序必须匹配:
DISTINCT ON要求排序的第一列必须是去重列(这里是employee_id),后续排序字段(created_at DESC)用来确定每组保留哪条记录; - 最终生成的SQL:这个Specification会生成和你手动写的原生SQL完全一致的语句:
SELECT DISTINCT ON (employee_id) * FROM contracts ORDER BY employee_id, created_at DESC。
替代方案(如果不想依赖Hibernate API)
如果想避免和Hibernate耦合,也可以直接在Repository接口用@Query写原生SQL:
@Query(value = "SELECT DISTINCT ON (employee_id) * FROM contracts ORDER BY employee_id, created_at DESC", nativeQuery = true) List<Contract> findLatestByEmployeeId();
内容的提问来源于stack exchange,提问作者mgh
相关产品推荐
相关产品推荐

