Mối quan hệ một-một trong JPA

1. Giới thiệu

Trong hướng dẫn này, chúng ta sẽ xem xét các cách khác nhau để tạo ánh xạ một-một trong JPA.

Chúng tôi sẽ cần hiểu biết cơ bản về khuôn khổ Hibernate, vì vậy hãy xem Hướng dẫn về Hibernate 5 với Spring của chúng tôi để biết thêm thông tin cơ bản.

2. Mô tả

Giả sử chúng ta đang xây dựng Hệ thống quản lý người dùng và sếp của chúng ta yêu cầu chúng ta lưu trữ địa chỉ gửi thư cho mỗi người dùng. Một người dùng sẽ có một địa chỉ gửi thư và một địa chỉ gửi thư sẽ chỉ có một người dùng gắn liền với nó.

Đây là một ví dụ về mối quan hệ một-một, trong trường hợp này là giữa các thực thể người dùngđịa chỉ .

Hãy xem cách chúng ta có thể thực hiện điều này trong các phần tiếp theo.

3. Sử dụng khóa ngoại

3.1. Lập mô hình bằng Khoá ngoại

Hãy xem sơ đồ ER sau đại diện cho một ánh xạ một-một dựa trên khóa ngoại:

Trong ví dụ này, cột address_id trong người dùng là khóa ngoại để địa chỉ .

3.2. Triển khai với Khóa ngoại trong JPA

Đầu tiên, hãy tạo lớp Người dùng và chú thích nó một cách thích hợp:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; // ... getters and setters } 

Lưu ý rằng chúng tôi đặt chú thích @OneToOne trên trường thực thể liên quan, Địa chỉ .

Ngoài ra, chúng ta cần đặt chú thích @JoinColumn để định cấu hình tên của cột trong bảng người dùng ánh xạ đến khóa chính trong bảng địa chỉ . Nếu chúng tôi không cung cấp tên, thì Hibernate sẽ tuân theo một số quy tắc để chọn một tên mặc định.

Cuối cùng, hãy lưu ý trong thực thể tiếp theo rằng chúng tôi sẽ không sử dụng chú thích @JoinColumn ở đó. Điều này là do chúng tôi chỉ cần nó ở khía cạnh sở hữu của mối quan hệ khóa ngoài. Nói một cách đơn giản, bất kỳ ai sở hữu cột khóa ngoại sẽ nhận được chú thích @JoinColumn .

Thực thể Địa chỉ hóa ra đơn giản hơn một chút:

@Entity @Table(name = "address") public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "address") private User user; //... getters and setters }

Chúng tôi cũng cần đặt chú thích @OneToOne ở đây. Đó là bởi vì đây là mối quan hệ hai chiều. Phía địa chỉ của mối quan hệ được gọi là phía không sở hữu .

4. Sử dụng khóa chính dùng chung

4.1. Tạo mô hình bằng Khóa chính dùng chung

Trong chiến lược này, thay vì tạo address_id cột mới , chúng tôi sẽ đánh dấu khóa chínhcột (user_id) của bảng địa chỉ làm khóa ngoại cho bảng người dùng :

Chúng tôi đã tối ưu hóa không gian lưu trữ bằng cách tận dụng thực tế là các thực thể này có mối quan hệ 1-1 giữa chúng.

4.2. Triển khai với Khóa chính dùng chung trong JPA

Lưu ý rằng các định nghĩa của chúng tôi chỉ thay đổi một chút:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address; //... getters and setters }
@Entity @Table(name = "address") public class Address { @Id @Column(name = "user_id") private Long id; //... @OneToOne @MapsId @JoinColumn(name = "user_id") private User user; //... getters and setters } 

Các mappedBy thuộc tính hiện nay được chuyển sang tài khoản lớp kể từ khi chính nước ngoài tại là hiện diện trong địa chỉ bảng. Chúng tôi cũng đã thêm các @PrimaryKeyJoinColumn chú thích, mà chỉ ra rằng chìa khóa chính của tài khoản thực thể được sử dụng như các giá trị chính nước ngoài để liên Địa chỉ đơn vị .

Chúng tôi vẫn phải xác định trường @Id trong lớp Địa chỉ nhưng lưu ý rằng trường này tham chiếu đến cột user_id và nó không còn sử dụng chú thích @GeneratedValue nữa . Ngoài ra, trên sân mà tài liệu tham khảo các tài khoản , chúng tôi đã thêm các @MapsId chú thích, mà chỉ ra rằng các giá trị khóa chính sẽ được sao chép từ các tài khoản thực thể.

5. Sử dụng một bảng tham gia

Ánh xạ một-một có thể có hai loại - Tùy chọnBắt buộc . Cho đến nay, chúng ta chỉ thấy những mối quan hệ bắt buộc.

Bây giờ, chúng ta hãy tưởng tượng rằng nhân viên của chúng tôi liên kết với một máy trạm. Đó là một đối một, nhưng đôi khi một nhân viên có thể không có máy trạm và ngược lại.

5.1. Lập mô hình với một bảng tham gia

Các chiến lược mà chúng ta đã thảo luận cho đến bây giờ buộc chúng ta phải đặt các giá trị null vào cột để xử lý các mối quan hệ tùy chọn .

Thông thường, chúng ta nghĩ đến các mối quan hệ nhiều-nhiều khi chúng ta xem xét một bảng nối, nhưng, trong trường hợp này, việc sử dụng một bảng nối, có thể giúp chúng ta loại bỏ các giá trị null này:

Bây giờ, bất cứ khi nào chúng ta có một mối quan hệ, chúng ta sẽ tạo một mục nhập trong bảng emp_workstation và tránh nullhoàn toàn.

5.2. Triển khai với Bảng tham gia trong JPA

Ví dụ đầu tiên của chúng tôi sử dụng @JoinColumn. Lần này, chúng tôi sẽ sử dụng @JoinTable :

@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "emp_workstation", joinColumns = { @JoinColumn(name = "employee_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "workstation_id", referencedColumnName = "id") }) private WorkStation workStation; //... getters and setters }
@Entity @Table(name = "workstation") public class WorkStation { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "workStation") private Employee employee; //... getters and setters }

@ JoinTable hướng dẫn Hibernate sử dụng chiến lược tham gia bảng trong khi duy trì mối quan hệ.

Ngoài ra, Nhân viên là chủ sở hữu của mối quan hệ này vì chúng tôi đã chọn sử dụng chú thích bảng tham gia trên đó.

6. Kết luận

Trong hướng dẫn này, chúng tôi đã tìm hiểu các cách khác nhau để duy trì liên kết một-một trong JPA và Hibernate và khi nào sử dụng mỗi cách.

Mã nguồn của hướng dẫn này có thể được tìm thấy trên GitHub.