1. 概述
在开发中,我们经常需要从数据库获取唯一数据。本文重点介绍如何使用Spring Data JPA实现数据去重查询,涵盖获取唯一实体和特定字段的多种方法。
2. 场景搭建
首先创建两个实体类:School
(学校)和Student
(学生)用于演示:
@Entity
@Table(name = "school")
public class School {
@Id
@Column(name = "school_id")
@GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
@Column(name = "name", length = 100)
private String name;
@OneToMany(mappedBy = "school")
private List<Student> students;
// 构造函数、getter和setter
}
@Entity
@Table(name = "student")
public class Student {
@Id
@Column(name = "student_id")
private int id;
@Column(name = "name", length = 100)
private String name;
@Column(name = "birth_year")
private int birthYear;
@ManyToOne
@JoinColumn(name = "school_id", referencedColumnName = "school_id")
private School school;
// 构造函数、getter和setter
}
我们定义了一对多关系:每个学校关联多个学生。
3. 去重实体查询
现在创建Repository接口,根据学生出生年份获取唯一学校数据。Spring Data JPA提供多种实现方式,第一种是使用派生查询:
@Repository
public interface SchoolRepository extends JpaRepository<School, Integer> {
List<School> findDistinctByStudentsBirthYear(int birthYear);
}
这个派生查询直观易懂,能根据学生出生年份获取唯一的School
实体。调用方法时,控制台会输出JPA生成的SQL(注意关联字段未被查询):
Hibernate:
select
distinct s1_0.school_id,
s1_0.name
from
school s1_0
left join
student s2_0
on s1_0.school_id=s2_0.school_id
where
s2_0.birth_year=?
✅ 如果只需要去重计数而非完整实体,只需将方法名中的find
替换为count
:
Long countDistinctByStudentsBirthYear(int birthYear);
4. 自定义查询实现字段去重
⚠️ 某些场景下不需要查询实体所有字段(比如Web界面搜索结果)。此时可通过限制查询字段提升性能,特别是在结果集较大时。
假设我们只需要获取唯一的学校名称,使用@Query
创建自定义查询:
@Query("SELECT DISTINCT sch.name FROM School sch JOIN sch.students stu WHERE stu.birthYear = :birthYear")
List<String> findDistinctSchoolNameByStudentsBirthYear(@Param("birthYear") int birthYear);
执行时会生成更高效的SQL(只查询name
字段):
Hibernate:
select
distinct s1_0.name
from
school s1_0
join
student s2_0
on s1_0.school_id=s2_0.school_id
where
s2_0.birth_year=?
5. 投影实现字段去重
Spring Data JPA的投影(Projections)功能是自定义查询的替代方案,允许获取实体部分字段而非全部。
创建投影接口(注意方法名必须与实体getter一致):
public interface NameView {
String getName();
}
在Repository中添加查询方法:
List<NameView> findDistinctNameByStudentsBirthYear(int birthYear);
生成的SQL与自定义查询版本类似:
Hibernate:
select
distinct s1_0.name
from
school s1_0
left join
student s2_0
on s1_0.school_id=s2_0.school_id
where
s2_0.birth_year=?
6. 总结
本文探讨了Spring Data JPA实现数据去重的三种方案:
- 派生查询:适合获取完整去重实体
- 自定义查询:精准控制查询字段
- 投影:声明式字段映射
根据实际需求选择合适方案,避免踩坑。完整代码示例可在GitHub获取。