使用 Spring Data JPA 尋找不同的行
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;
// constructors, getters and setters
}
@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;
// constructors, getters and setters
}
我們定義了一對多關係,其中每個學校與多個學生相關聯。
3. 不同的實體
我們的範例實體現已設定。讓我們繼續建立一個儲存庫,用於按學生的出生年份檢索不同的學校。使用 Spring Data JPA 取得不同行的方法有多種。第一個是使用派生查詢:
@Repository
public interface SchoolRepository extends JpaRepository<School, Integer> {
List<School> findDistinctByStudentsBirthYear(int birthYear);
}
派生查詢是不言自明的並且易於理解。它按Student的出生年份找到所有不同的School實體。如果我們呼叫該方法,我們將在控制台日誌中看到 JPA 在School實體上執行的 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註釋該方法並將 JPQL 查詢放入其中。我們透過@Param註解將出生年份參數傳遞到 JPQL 中:
@Query("SELECT DISTINCT sch.name FROM School sch JOIN sch.students stu WHERE stu.birthYear = :birthYear")
List<String> findDistinctSchoolNameByStudentsBirthYear(@Param("birthYear") int birthYear);
執行後,我們將在控制台日誌中看到JPA產生的以下SQL。只涉及學校名稱字段,不涉及所有字段:
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 查詢方法通常使用實體作為傳回類型。但是,我們可以應用 Spring Data 提供的投影作為自訂查詢方法的替代方案。這允許我們從實體中檢索部分欄位而不是全部欄位。
由於我們希望僅檢索學校名稱字段,因此我們將建立一個接口,其中包含School實體中名稱欄位的 getter 方法:
public interface NameView {
String getName();
}
投影介面中的方法名稱必須與目標實體中的 getter 方法名稱相同。畢竟,我們必須將以下方法新增到儲存庫中:
List<NameView> findDistinctNameByStudentsBirthYear(int birthYear);
執行後,我們將看到產生的 SQL 與自訂查詢中產生的 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=?
六,結論
在本文中,我們探索了透過 Spring Data JPA 從資料庫查詢不同行的不同方法,包括不同的實體和不同的欄位。我們可以根據需要使用不同的方法。
與往常一樣,完整的源代碼可以在 GitHub 上取得。