Bi-directional Relationship trong Hibernate
1. Giới thiệu về Bi-directional Relationship
Bi-directional Relationship là gì? Bi-directional Relationship là một mối quan hệ trong Hibernate mà cả hai phía của mối quan hệ đều biết về sự tồn tại của nhau. Điều này có nghĩa là nếu có hai entity A và B có mối quan hệ bi-directional, thì entity A có thể tham chiếu đến entity B và ngược lại.
Tại sao cần Bi-directional Relationship? Bi-directional Relationship giúp đảm bảo tính toàn vẹn của dữ liệu và dễ dàng hơn trong việc truy xuất và quản lý dữ liệu liên quan. Nó cũng giúp giảm thiểu lỗi và tăng tính rõ ràng của mã nguồn.
2. Ví dụ về Bi-directional One-to-Many và Many-to-One
Ví dụ cụ thể về One-to-Many và Many-to-One Giả sử chúng ta có hai entity User
và Address
với mối quan hệ One-to-Many. Một User
có thể có nhiều Address
và một Address
thuộc về một User
.
Entity User
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Address> addresses = new HashSet<>();
// Getters and setters
public void addAddress(Address address) {
addresses.add(address);
address.setUser(this);
}
public void removeAddress(Address address) {
addresses.remove(address);
address.setUser(null);
}
}
Entity Address
@Entity
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "street")
private String street;
@Column(name = "city")
private String city;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
// Getters and setters
}
Chú thích:
Trong
User
, thuộc tínhaddresses
sử dụng annotation@OneToMany
với thuộc tínhmappedBy
chỉ định rằng mối quan hệ này được ánh xạ bởi thuộc tínhuser
trongAddress
.Trong
Address
, thuộc tínhuser
sử dụng annotation@ManyToOne
với@JoinColumn
để xác định cột khóa ngoạiuser_id
.
Thao tác thêm và xóa địa chỉ từ người dùng
User user = new User();
user.setUsername("johndoe");
user.setPassword("password123");
Address address1 = new Address();
address1.setStreet("123 Main St");
address1.setCity("Hometown");
Address address2 = new Address();
address2.setStreet("456 Elm St");
address2.setCity("Big City");
user.addAddress(address1);
user.addAddress(address2);
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.save(user);
transaction.commit();
session.close();
Trong ví dụ này, chúng ta tạo một đối tượng User
và hai đối tượng Address
, sau đó thêm các địa chỉ vào người dùng. Khi lưu người dùng, Hibernate sẽ tự động lưu các địa chỉ liên quan.
3. Ví dụ về Bi-directional Many-to-Many
Ví dụ cụ thể về Many-to-Many Giả sử chúng ta có hai entity Student
và Course
với mối quan hệ Many-to-Many. Một Student
có thể tham gia nhiều Course
và một Course
có thể có nhiều Student
.
Entity Student
@Entity
@Table(name = "students")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@ManyToMany
@JoinTable(
name = "student_courses",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
// Getters and setters
public void addCourse(Course course) {
courses.add(course);
course.getStudents().add(this);
}
public void removeCourse(Course course) {
courses.remove(course);
course.getStudents().remove(this);
}
}
Entity Course
@Entity
@Table(name = "courses")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "title")
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
// Getters and setters
}
Chú thích:
Trong
Student
, thuộc tínhcourses
sử dụng annotation@ManyToMany
với@JoinTable
để xác định bảng trung gianstudent_courses
và các cột khóa ngoạistudent_id
vàcourse_id
.Trong
Course
, thuộc tínhstudents
sử dụng annotation@ManyToMany
với thuộc tínhmappedBy
chỉ định rằng mối quan hệ này được ánh xạ bởi thuộc tínhcourses
trongStudent
.
Thao tác thêm và xóa khóa học từ sinh viên
Student student = new Student();
student.setName("John Doe");
Course course1 = new Course();
course1.setTitle("Mathematics");
Course course2 = new Course();
course2.setTitle("Physics");
student.addCourse(course1);
student.addCourse(course2);
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.save(student);
transaction.commit();
session.close();
Trong ví dụ này, chúng ta tạo một đối tượng Student
và hai đối tượng Course
, sau đó thêm các khóa học vào sinh viên. Khi lưu sinh viên, Hibernate sẽ tự động lưu các khóa học liên quan và cập nhật bảng trung gian student_courses
.
4. Tóm tắt và lưu ý khi sử dụng Bi-directional Relationship
Đảm bảo tính toàn vẹn: Sử dụng các phương thức tiện ích như
addAddress
,removeAddress
,addCourse
, vàremoveCourse
để đảm bảo rằng cả hai phía của mối quan hệ đều được cập nhật chính xác.Lazy vs Eager Fetching: Chọn chiến lược fetching phù hợp để tối ưu hóa hiệu suất và tránh các vấn đề về hiệu suất hoặc
LazyInitializationException
.Mapped By: Luôn sử dụng thuộc tính
mappedBy
để chỉ định phía chủ động của mối quan hệ, giúp Hibernate hiểu rõ hơn về cấu trúc và quản lý mối quan hệ hiệu quả.
Bi-directional Relationship là một kỹ thuật quan trọng trong Hibernate, giúp quản lý và truy xuất dữ liệu liên quan một cách hiệu quả và rõ ràng. Việc hiểu và áp dụng đúng kỹ thuật này sẽ giúp bạn xây dựng các ứng dụng Java sử dụng Hibernate mạnh mẽ và tối ưu hơn.