Hubungan Satu-ke-Satu di JPA

1. Perkenalan

Dalam tutorial ini, kita akan melihat berbagai cara membuat pemetaan satu-ke-satu di JPA.

Kami membutuhkan pemahaman dasar tentang framework Hibernate, jadi silakan lihat Panduan kami untuk Hibernate 5 dengan Spring untuk latar belakang tambahan.

2. Deskripsi

Misalkan kita sedang membangun Sistem Manajemen Pengguna dan atasan kita meminta kita untuk menyimpan alamat surat untuk setiap pengguna. Seorang pengguna akan memiliki satu alamat surat, dan satu alamat surat hanya akan memiliki satu pengguna yang terikat padanya.

Ini adalah contoh hubungan satu-ke-satu, dalam hal ini antara entitas pengguna dan alamat .

Mari kita lihat bagaimana kita dapat menerapkan ini di bagian selanjutnya.

3. Menggunakan Kunci Asing

3.1. Pemodelan dengan Kunci Asing

Mari kita lihat diagram ER berikut yang mewakili pemetaan satu-ke-satu berbasis kunci asing:

Dalam contoh ini, kolom address_id di users adalah kunci asing ke alamat .

3.2. Menerapkan dengan Kunci Asing di JPA

Pertama, mari buat kelas User dan beri anotasi dengan tepat:

@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 } 

Perhatikan bahwa kami menempatkan anotasi @OneToOne di bidang entitas terkait, Alamat .

Selain itu, kita perlu menempatkan anotasi @JoinColumn untuk mengonfigurasi nama kolom di tabel pengguna yang memetakan ke kunci utama di tabel alamat . Jika kami tidak memberikan nama, Hibernate akan mengikuti beberapa aturan untuk memilih yang default.

Terakhir, perhatikan di entitas berikutnya bahwa kami tidak akan menggunakan anotasi @JoinColumn di sana. Ini karena kami hanya membutuhkannya di sisi pemilik hubungan kunci asing. Sederhananya, siapa pun yang memiliki kolom kunci asing akan mendapatkan anotasi @JoinColumn .

The Alamat entitas ternyata sedikit lebih sederhana:

@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 }

Kita juga perlu menempatkan anotasi @OneToOne di sini juga. Itu karena ini adalah hubungan dua arah. Sisi alamat dari hubungan disebut sisi tidak memiliki .

4. Menggunakan Kunci Utama Bersama

4.1. Pemodelan dengan Kunci Utama Bersama

Dalam strategi ini, alih-alih membuat kolom address_id baru, kami akan menandai kunci utamakolom (user_id) dari tabel alamat sebagai kunci asing ke tabel pengguna :

Kami telah mengoptimalkan ruang penyimpanan dengan memanfaatkan fakta bahwa entitas ini memiliki hubungan satu-ke-satu di antara mereka.

4.2. Menerapkan dengan Kunci Utama Bersama di JPA

Perhatikan bahwa definisi kami hanya berubah sedikit:

@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 } 

The mappedBy atribut sekarang pindah ke Pengguna kelas sejak kunci asing kini hadir di alamat tabel. Kami juga telah menambahkan satu @PrimaryKeyJoinColumn penjelasan, yang menunjukkan bahwa kunci utama dari pengguna entitas digunakan sebagai nilai kunci asing untuk terkait Alamat entitas .

Kita masih harus mendefinisikan bidang @Id di kelas Alamat tetapi perhatikan ini merujuk pada kolom user_id , dan tidak lagi menggunakan anotasi @GeneratedValue . Juga, di lapangan bahwa referensi yang Pengguna , kami telah menambahkan satu @MapsId penjelasan, yang menunjukkan bahwa nilai-nilai kunci primer akan disalin dari Pengguna entitas.

5. Menggunakan Tabel Gabungan

Pemetaan satu-ke-satu dapat terdiri dari dua jenis - Opsional dan Wajib . Sejauh ini, kami hanya melihat hubungan wajib.

Sekarang, mari kita lihat bayangkan bahwa karyawan kita terkait dengan workstation. Ini satu-ke-satu, tetapi terkadang seorang karyawan mungkin tidak memiliki tempat kerja dan sebaliknya.

5.1. Memodelkan dengan Tabel Bergabung

Strategi yang telah kita bahas sampai sekarang memaksa kita untuk meletakkan nilai null di kolom untuk menangani hubungan opsional .

Biasanya, kita memikirkan hubungan banyak-ke-banyak ketika kita mempertimbangkan tabel gabungan, tetapi, menggunakan tabel gabungan, dalam hal ini, dapat membantu kita menghilangkan nilai-nilai nol ini:

Sekarang, setiap kali kita memiliki hubungan, kita akan membuat entri di tabel emp_workstation dan menghindari nullssama sekali.

5.2. Menerapkan dengan Tabel Gabung di JPA

Contoh pertama kami menggunakan @JoinColumn. Kali ini, kami akan menggunakan @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 menginstruksikan Hibernate untuk menggunakan strategi tabel gabung sambil mempertahankan hubungan.

Selain itu, Karyawan adalah pemilik hubungan ini karena kami memilih untuk menggunakan anotasi tabel gabungan di atasnya.

6. Kesimpulan

Dalam tutorial ini, kami mempelajari berbagai cara untuk mempertahankan pengaitan satu-ke-satu di JPA dan Hibernate dan kapan harus menggunakannya.

Kode sumber dari tutorial ini dapat ditemukan di GitHub.