了解 JPA/Hibernate 關聯
一、概述
Java Persistence API (JPA) 是一種用於 Java 應用程序的對象關係映射 (ORM) 規範。此外,Hibernate 是 JPA 規範的流行實現之一。
關聯是 ORM 中的一個基本概念,允許我們定義實體之間的關係。在本教程中,我們將討論 JPA/Hibernate 中單向關聯和雙向關聯之間的區別。
2.單向關聯
單向關聯通常用於面向對象編程以建立實體之間的關係。但是,請務必注意,在單向關聯中,只有一個實體持有對另一個實體的引用。
要在 Java 中定義單向關聯,我們可以使用諸如@ManyToOne
、 @OneToMany
、 @OneToOne
和@ManyToMany
之類的註釋。通過使用這些註釋,我們可以在代碼中的兩個實體之間創建清晰且定義明確的關係。
2.1.一對多關係
在一對多關係中,一個實體引用另一個實體的一個或多個實例。
一個常見的例子是Department
與其Employee
之間的關係。每個Department
都有很多Employee
,但是每個Employee
只屬於一個Department
。
我們來看看如何定義一對多的單向關聯:
@Entity
public class Department {
@Id
private Long id;
@OneToMany
@JoinColumn(name = "department_id")
private List<Employee> employees;
}
@Entity
public class Employee {
@Id
private Long id;
}
此處, Department
實體引用了Employee
實體列表。 @OneToMany
註釋指定這是一個一對多關聯。 @JoinColumn
註解指定了Employee
表中引用Department
表的外鍵列。
2.2.多對一關係
在多對一關係中,一個實體的多個實例與另一個實體的一個實例相關聯。
例如,讓我們考慮Student
和School.
每個Student
只能註冊一所School
,但每個School
可以有多個Student
。
我們來看看如何定義多對一的單向關聯:
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "school_id")
private School school;
}
@Entity
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
在這種情況下,我們在Student
和School
實體之間建立了多對一的單向關聯。 @ManyToOne
註解指定每個學生只能註冊一所學校, @JoinColumn
註解指定外鍵列名加入Student
和School
實體。
2.3.一對一關係
在一對一關係中,一個實體的實例僅與另一個實體的一個實例相關聯。
一個常見的例子是Employee
和ParkingSpot
之間的關係。每個Employee
都有一個ParkingSpot
,每個ParkingSpot
都屬於一個Employee
。
我們來看看如何定義一對一的單向關聯:
@Entity
public class Employee {
@Id
private Long id;
@OneToOne
@JoinColumn(name = "parking_spot_id")
private ParkingSpot parkingSpot;
}
@Entity
public class ParkingSpot {
@Id
private Long id;
}
此處, Employee
實體引用了ParkingSpot
實體。 @OneToOne
註釋指定這是一個一對一的關聯。 @JoinColumn
註解指定Employee
表中引用ParkingSpot
表的外鍵列
2.4.多對多關係
在多對多關係中,一個實體的多個實例與另一個實體的多個實例相關聯。
假設我們有兩個實體—— Book
和Author
。每Book
可以有多個Author
,每個Author
可以寫多Book
。在 JPA 中,此關係使用@ManyToMany
註釋表示。
我們來看看如何定義多對多的單向關聯:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany
@JoinTable(name = "book_author",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "author_id"))
private Set<Author> authors;
}
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
在這裡,我們可以看到Book
和Author
實體之間的多對多單向關聯。 @ManyToMany
註解指定每本Book
可以有多個Author
,每個Author
可以寫多本Book
。 @JoinTable
註釋指定連接表的名稱和用於連接Book
和Author
實體的外鍵列。
3.雙向關聯
雙向關聯是兩個實體之間的關係,其中每個實體都引用另一個實體。
為了定義雙向關聯,我們在@OneToMany
和@ManyToMany
註釋中使用mappedBy
屬性。但是,請務必注意,僅依靠單向關聯可能還不夠,因為雙向關聯提供了額外的好處。
3.1.一對多雙向關聯
在一對多雙向關聯中,一個實體具有對另一個實體的引用。此外,另一個實體具有對第一個實體的引用集合。
例如,一個Department
實體有一個Employee
實體的集合。同時, Employee
實體引用了它所屬的Department
實體。
我們來看看如何創建一對多的雙向關聯:
@Entity
public class Department {
@OneToMany(mappedBy = "department")
private List<Employee> employees;
}
@Entity
public class Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}
在 Department 實體中,我們使用@OneToMany
註解來指定Department
實體和Employee
實體之間的關係。 mappedBy
屬性指定擁有該關係的Employee
實體中的屬性名稱。在這種情況下, Department
實體不擁有該關係,因此我們指定mappedBy = “department”
。
在Employee
實體中,我們使用@ManyToOne
註解來指定Employee
實體和Department
實體之間的關係。 @JoinColumn
註釋指定引用Department
表的Employee
表中的外鍵列的名稱。
3.2.多對多雙向關聯
在處理多對多雙向關聯時,重要的是要了解所涉及的每個實體都將擁有對另一個實體的引用集合。
為了說明這個概念,讓我們考慮一個Student
實體的示例,該 Student 實體具有Course
實體的集合,而Course
實體又具有Student
實體的集合。通過建立這樣的雙向關聯,我們使兩個實體能夠相互了解並更容易導航和管理它們的關係。
以下是如何創建多對多雙向關聯的示例:
@Entity
public class Student {
@ManyToMany(mappedBy = "students")
private List<Course> courses;
}
@Entity
public class Course {
@ManyToMany
@JoinTable(name = "course_student",
joinColumns = @JoinColumn(name = "course_id"),
inverseJoinColumns = @JoinColumn(name = "student_id"))
private List<Student> students;
}
在Student
實體中,我們使用@ManyToMany
註解來指定Student
實體和Course
實體之間的關係。 mappedBy
屬性在擁有該關係的Course
實體中指定該屬性的名稱。在這種情況下, Course
實體擁有該關係,因此我們指定mappedBy = “students”
。
在Course
實體中,我們使用@ManyToMany
註解來指定Course
實體和Student
實體之間的關係.
@JoinTable
註釋指定存儲關係的連接表的名稱。
4. 單向與雙向關聯
面向對象編程中的單向關聯和雙向關聯在兩個類之間關係的方向上有所不同。
首先,單向關聯只在一個方向上有關係,而雙向關聯在兩個方向上都有關係。這種差異會影響軟件系統的設計和功能。例如,雙向關聯可以更輕鬆地在相關類之間導航,但它們也會帶來更多的複雜性和潛在的錯誤。
另一方面,單向關聯可以更簡單且不易出錯,但它們可能需要更多變通方法才能在相關類之間導航。
總的來說,了解單向關聯和雙向關聯之間的差異對於做出有關軟件系統的設計和實現的明智決策至關重要。
下表總結了數據庫中單向關聯和雙向關聯之間的區別:
單向關聯 | 雙向關聯 | |
定義 | 兩個表之間的關係,其中一個表具有引用另一個表的主鍵的外鍵。 | 兩個表之間的關係,其中兩個表都有引用另一個表的主鍵的外鍵。 |
導航 | 只能在一個方向上導航——從子表到父表。 | 可雙嚮導航——從任一表格到另一表格。 |
表現 | 由於更簡單的表結構和更少的約束,通常速度更快。 | 由於額外的約束和表結構的複雜性,通常速度較慢。 |
數據一致性 | 通過引用父表中主鍵的子表中的外鍵約束來確保。 | 通過引用父表中主鍵的子表中的外鍵約束來確保。 |
靈活性 | 不太靈活,因為子表中的更改可能需要更改父表架構。 | 更靈活,因為可以在不影響另一個的情況下獨立地對任何一個表進行更改。 |
值得注意的是,實施細節可能因所使用的數據庫管理系統而異。但是,為了提供一般理解,上表概述了單向關聯和雙向關聯之間的差異。
認識到這些變化很重要,因為它們會顯著影響數據庫系統的性能和功能。
5.結論
在本文中,我們了解瞭如何根據軟件的特定需求來選擇單向關聯或雙向關聯。單向關聯更簡單,可能足以滿足許多應用程序的需求,而雙向關聯提供了更大的靈活性,可用於更複雜的場景。
但是,雙向關聯也會引入更多的複雜性和潛在問題,例如循環依賴和內存洩漏,應謹慎使用。仔細考慮權衡並為每種情況選擇適當的關聯類型很重要。
與往常一樣,代碼 在 GitHub 上可用。