One-to-One Cascade
Pada materi sebelumnya kita sudah membahas tentang Cascading. Pada materi ini kita akan mengimplementasikan penggunaan Cascade Delete pada project One-to-One antara Classes dan Teacher.
1️⃣ Menggunakan Cascade ALL (Agresif)
⚙️ Setup Entity
- Menggunakan Cascade
Allakan membuat Child(Classes) tidak bisa hidup tanpa Parent (Teacher). Jadi efeknya ketika kita memanggilentityManager.remove(teacher1)untuk menghapus dataTeacher, maka dataClassesyang terhubung juga akan hilang dari database.
- Pada
Teacher.java:
@OneToOne(mappedBy = "teacher", cascade = CascadeType.ALL)
private Classes classes;
🔁 Setup DAO
- Menambahkan method
deleteTeacherById(Integer id)pada Interface DAO:
public interface SchoolDAO {
void saveClasses(Classes classes);
Classes findClassById(Integer id);
void deleteClassesById(Integer id);
Teacher findTeacherById(Integer id);
void deleteTeacherById(Integer id);
}
- Implementasi DAO:
@Override
@Transactional
public void deleteTeacherById(Integer id) {
entityManager.remove(entityManager.find(Teacher.class, id));
}
🏃 Main App
- Kita akan melakukan penghapusan data dengan Cascade ALL di mana ketika menghapus data pada
Teacherakan menghapus juga data padaClasses. - Contoh kita punya data
TeacherdanClassesdalam databases:
mysql> select * from teacher;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Andi | Fahrum |
| 2 | Budi | Sudarsono |
| 3 | Beddu | Kendeng |
+----+------------+-----------+
3 rows in set (0.00 sec)
mysql> select * from classes;
+----+------------+------------+
| id | class_name | teacher_id |
+----+------------+------------+
| 1 | Kelas 9 | 1 |
| 2 | Kelas 10 | 2 |
| 3 | Kelas 11 | 3 |
+----+------------+------------+
3 rows in set (0.00 sec)
- Pada Class Main kita akan mencoba menghapus data
Teacherdengan ID 2, maka seharusnya dataClassesterkait juga akan terhapus:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// Bean ini akan dieksekusi di command line
@Bean
public CommandLineRunner commandLineRunner(SchoolDAO dao) {
return runner -> {
deleteTeacher(dao); // delete bi-directional (Cascade ALL)
};
}
private void deleteTeacher(SchoolDAO dao) {
Integer id = 2;
dao.deleteTeacherById(id);
System.out.println("Teacher deleted with id " + id);
}
}
- Output Hibernate SQL:
Hibernate: select t1_0.id,c1_0.id,c1_0.class_name,t1_0.first_name,t1_0.last_name from teacher t1_0 left join classes c1_0 on t1_0.id=c1_0.teacher_id where t1_0.id=?
Hibernate: delete from classes where id=?
Hibernate: delete from teacher where id=?
Teacher deleted with id 2
- Cek data dalam database:
mysql> select * from teacher;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Andi | Fahrum |
| 3 | Beddu | Kendeng |
+----+------------+-----------+
2 rows in set (0.00 sec)
mysql> select * from classes;
+----+------------+------------+
| id | class_name | teacher_id |
+----+------------+------------+
| 1 | Kelas 9 | 1 |
| 3 | Kelas 11 | 3 |
+----+------------+------------+
2 rows in set (0.00 sec)
2️⃣ Menggunakan Cascade Terpilih (Spesifik)
Contoh kasus kita ingin data Classes (Parent) otomatis melakukan simpan, update, reload dan detach, tapi tidak ingin otomatis menghapus data Teacher (Child). Karena dalam kasus ini secara logika ketika kelas(Classes) dihapus data guru(Teacher) jangan ikut terhapus, karena bisa saja guru suatu saat akan menjadi walikelas di kelas lain.
⚙️ Setup Entity
- Pada bagian
cascade,CascadeTypeharus diisi manual secara spesifik, disini kita tidak memasukanCascadeType.REMOVE.
@OneToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinColumn(name = "teacher_id")
private Teacher teacher;
- Efek: Menyimpan, update, reload dan detach pada kelas(
Classes) akan otomatis terjadi juga pada guru(Teacher). Namun, menghapus kelas(Classes) tidak akan menghapus gurunya(Teacher). Ini lebih aman untuk data referensi seperti Guru.
🔁 Setup DAO
- Pada Interface DAO:
public interface SchoolDAO {
// ...
void deleteClassesById(Integer id);
// ...
}
- Implementasi DAO:
@Override
@Transactional
public void deleteClassesById(Integer id) {
Classes classes = entityManager.find(Classes.class, id);
// remove the associated object reference and break bi-directional link
classes.getTeacher().setClasses(null);
entityManager.remove(classes);
}
🏃 Main App
- Kita akan melakukan penghapusan data dimana ketika menghapus data pada
Classestidak akan menghapus data padaTeacher. - Contoh kita punya data
TeacherdanClassesdalam databases:
mysql> select * from teacher;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Andi | Fahrum |
| 3 | Beddu | Kendeng |
+----+------------+-----------+
2 rows in set (0.00 sec)
mysql> select * from classes;
+----+------------+------------+
| id | class_name | teacher_id |
+----+------------+------------+
| 1 | Kelas 9 | 1 |
| 3 | Kelas 11 | 3 |
+----+------------+------------+
- Pada Class Main kita akan mencoba menghapus data
Classesdengan ID 1, maka seharusnya dataTeacherterkait tidak akan terhapus:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// Bean ini akan dieksekusi di command line
@Bean
public CommandLineRunner commandLineRunner(SchoolDAO dao) {
return runner -> {
deleteClasses(dao);
};
}
private void deleteClasses(SchoolDAO dao) {
Integer id = 1;
dao.deleteClassesById(id);
System.out.println("Classes deleted with id " + id);
}
}
- Output Hibernate SQL:
Hibernate: select c1_0.id,c1_0.class_name,t1_0.id,t1_0.first_name,t1_0.last_name from classes c1_0 left join teacher t1_0 on t1_0.id=c1_0.teacher_id where c1_0.id=?
Hibernate: delete from classes where id=?
Classes deleted with id 1
- Cek data dalam database:
mysql> select * from teacher;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Andi | Fahrum |
| 3 | Beddu | Kendeng |
+----+------------+-----------+
2 rows in set (0.00 sec)
mysql> select * from classes;
+----+------------+------------+
| id | class_name | teacher_id |
+----+------------+------------+
| 3 | Kelas 11 | 3 |
+----+------------+------------+
1 row in set (0.00 sec)
- Dapat terlihat di atas bahwa data pada tabel
Teachertetap ada walaupun data pada tabelClassesdengan id 1 sudah terhapus.
🔥 Kesimpulan: Kapan Harus Menggunakan Cascading?
- Gunakan Cascading jika ada hubungan ketergantungan yang kuat (Parent-Child).
- Contoh: Menghapus sebuah
Accountmaka harus menghapusAccountSettings.
- Contoh: Menghapus sebuah
- Jangan Gunakan Cascading REMOVE jika data Child masih dibutuhkan oleh entitas lain atau merupakan data master.
- Contoh: Jangan hapus Guru (
Teacher) hanya karena kelasnya (Classes) dibubarkan, karena Guru (Teacher) tersebut mungkin mengajar di kelas (Classes) lain.
- Contoh: Jangan hapus Guru (