Implementasi CRUD Menggunakan Java Persistance API dan Hibernate ORM
Halo semua, pada artikel kali ini kita akan membahas salah satu cara implementasi CRUD (Create, Read, Update, Delete) pada Java dan database MySQL menggunakan Spring JPA dan Hibernate ORM.
Bagi yang baru mengenali istilah CRUD, ini adalah operasi dasar dari sebagian besar aplikasi berbasis data dan menyediakan fungsionalitas untuk berinteraksi dengan informasi dalam sistem perangkat lunak. Operasi-operasi ini dapat diimplementasikan dalam aplikasi menggunakan bahasa pemrograman dan teknologi yang sesuai, seperti SQL untuk basis data relasional atau HTTP methods (GET, POST, PUT, DELETE) untuk API web. Sederhananya, CRUD merupakan operasi pengelolaan data dari sistem yang terhubung ke database.
Sementara itu, Spring Data JPA merupakan salah satu Spring Framework yang menyediakan implementasi JPA (Java Persistence API) berbasis Java untuk mengelola data di aplikasi Java. JPA adalah spesifikasi Java untuk manajemen objek-relasional (ORM) yang memungkinkan pengembang menggunakan objek Java secara langsung untuk berinteraksi dengan database dengan dukungan hibernate ORM. Hibernate sendiri adalah sebuah framework ORM (Object-Relational Mapping) yang digunakan dalam pengembangan aplikasi Java untuk mengelola interaksi dengan database. ORM adalah teknik pemetaan objek ke dalam bentuk data yang dapat disimpan dalam database relasional. Dengan menggunakan Hibernate, pengembang dapat menyimpan, mengambil, dan mengelola data objek Java dalam basis data relasional tanpa menulis SQL (Structured Query Language) secara langsung.
Setup Project
Untuk membuat project, Anda dapat menggunakan template dari https://start.spring.io/. Sesuaikan beberapa konfigurasi pada laman start spring yang anda inginkan. Lalu tambahkan beberapa dependencies seperti Spring Data JPA, MySQL Driver dan Lombok untuk generate method di entity (Baca juga: Implementasi Library Lombok untuk Efisiensi Kode di Java). Setelah itu buka project pada IDE yang Anda gunakan.
Di dalam directory src->resource, tambahkan directory baru dengan nama META-INF
dan didalam directory tersebut tambahkan juga file persistence.xml
untuk menyimpan konfigurasi antara aplikasi spring dengan database MySQL. Berikut isi dari file persistence.xml
:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="JPADB">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbjpa"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value=""/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
Elemen <persistence>
adalah elemen root dari file persistence.xml
dan menyatakan bahwa ini adalah file persistence config yang mengikuti spesifikasi XML dari Java Persistence API (JPA) versi 2.0.
Kemudian, terdapat elemen <persistence-unit name="JPADB">
yang mendefinisikan persistence unit dengan nama "JPADB" dan nama unit dapat diganti sesuai dengan keinginan Anda. Dalam konteks JPA, sebuah persistence unit adalah kumpulan objek yang berkaitan dengan pengaturan persistence, termasuk konfigurasi koneksi database dan pemetaan objek ke tabel database.
Di dalam <persistence-unit>
, terdapat elemen <provider>
yang menunjukkan persistence provider yang digunakan. Dalam kasus ini, persistence provider yang dipilih adalah org.hibernate.jpa.HibernatePersistenceProvider
, yang menandakan bahwa Hibernate digunakan sebagai implementasi JPA.
Selanjutnya, dalam elemen <properties>
, terdapat beberapa properti yang dikonfigurasi:
- Properti
jakarta.persistence.jdbc.driver
menentukan driver JDBC yang digunakan (dalam hal ini,com.mysql.cj.jdbc.Driver
). - Properti
jakarta.persistence.jdbc.url
menentukan URL koneksi ke database (dalam hal ini,jdbc:mysql://localhost:3306/dbjpa
mengarah ke database local MySQL dengan namadbjpa
). Sebaiknya, buat terlebih dahulu sebuah database dengan namadbjpa
lalu registrasikan pada properti ini. - Properti
jakarta.persistence.jdbc.user
menentukan nama pengguna untuk koneksi database (dalam hal ini,root
). - Properti
jakarta.persistence.jdbc.password
menentukan kata sandi untuk koneksi database (dalam hal ini, kosong, menandakan bahwa tidak ada kata sandi yang digunakan). - Properti
hibernate.show_sql
danhibernate.format_sql
mengatur apakah Hibernate akan menampilkan pernyataan SQL yang dihasilkan dan apakah SQL tersebut akan diformat untuk kenyamanan membaca.
Dengan konfigurasi ini, aplikasi yang menggunakan file persistence.xml
ini dapat menggunakan Hibernate sebagai penyedia JPA untuk berinteraksi dengan database MySQL yang disebutkan dalam properti-properti koneksi.
Setelah menambahkan konfigurasi database pada file persistence.xml
selanjutnya buat sebuah class entity pada project. Class Entity di JPA merupakan representasi dari sebuah table di database SQL. Berikut class entity yang sudah tambahkan:
@Data
@NoArgsConstructor
@Entity
@Table(name = "customers")
public class Customer {
@Id
private String id;
private String name;
@Column(name = "primary_email")
private String primaryEmail;
private Boolean married;
}
Pada class Customer.java
terdapat annotation @Data
dan @NoArgsConstructor
, dua annotation tersebut merupakan fitur dari Lombok library, (pastikan Anda sudah menambahkan Lombok library pada file pom.xml
) yang mana annotation @Data
merupakan generated annotation untuk method setter-getter, toString() dll tanpa perlu membuat setter-getter untuk seluruh field yang ditambahkan. Untuk annotation @NoArgsConstructor
digunakan untuk men-generated sebuah constructor kosong. Annotation @Entity
menunjukkan bahwa kelas ini adalah sebuah entitas yang akan disimpan dalam database berupa table. Sedangkan @Table(name = "customers")
menginformasikan bahwa entity ini akan disimpan dalam tabel bernama "customers" dalam database.
Tambahkan juga table dengan nama customers pada database local yang sudah Anda registrasikan pada file persistence.xml
CREATE TABLE customers (
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255),
primary_email VARCHAR(255),
married BOOLEAN
);
Selanjutnya kita dapat mengimplementasikan langsung operasi CRUD. Disini saya akan mengimplementasikan operasi CRUD menggunakan unit test. Buatlah sebuah class unit test dengan nama CrudTest.java
class CrudTest{
private EntityManagerFactory entityManagerFactory;
@BeforeEach
void setUp() {
entityManagerFactory = JpaUtil.getEntityManagerFactory();
}
}
Pertama, deklarasi private EntityManagerFactory entityManagerFactory;
mendefinisikan variabel entityManagerFactory
sebagai objek EntityManagerFactory
. EntityManagerFactory
adalah antarmuka dalam JPA yang bertanggung jawab untuk membuat objek EntityManager
. EntityManager
digunakan untuk berkomunikasi dengan basis data, termasuk operasi-insert, read, update, dan delete (CRUD).
Selanjutnya, menggunakan anotasi @BeforeEach
, metode setUp()
dijalankan sebelum setiap pengujian dimulai. Dalam metode ini, entityManagerFactory
diinisialisasi dengan menggunakan metode JpaUtil.getEntityManagerFactory()
. Fungsi dari setUp()
adalah untuk mempersiapkan lingkungan pengujian dengan menginisialisasi objek EntityManagerFactory
sebelum setiap pengujian dijalankan.
Berikut isi class dari JpaUtil.java
public class JpaUtil {
private static EntityManagerFactory entityManagerFactory = null;
/**
* jika entityManagerFactory null, maka buat entity melalui file persistence.xml,
* jika sudah ada, maka balikan nilai entityManagerFactory.
*/
public static EntityManagerFactory getEntityManagerFactory(){
if (entityManagerFactory == null){
entityManagerFactory = Persistence.createEntityManagerFactory("JPADB");
}
return entityManagerFactory;
}
}
Pada kelas JpaUtil
, terdapat sebuah variabel statis entityManagerFactory
yang bertipe EntityManagerFactory
. Variabel ini diset sebagai static
, artinya hanya ada satu salinan dari variabel ini untuk seluruh instansi kelas JpaUtil
yang ada dalam aplikasi. Hal ini memastikan bahwa entityManagerFactory
tetap konsisten di seluruh aplikasi dan menghindari pembuatan objek EntityManagerFactory
yang berlebihan. kelas JpaUtil
memastikan bahwa hanya satu objek EntityManagerFactory
yang dibuat, meminimalkan overhead dan memastikan konsistensi dalam penggunaan objek EntityManagerFactory
di seluruh aplikasi. Utilitas ini memudahkan pengelolaan objek EntityManagerFactory
dan memastikan koneksi yang efisien dan andal ke basis data saat berinteraksi dengan JPA.
Untuk implementasi operasi menggunakan CRUD, buatlah sebuah class unit test dengan nama CrudTest.java
dan tambahkan kode berikut.
class CrudTest{
private EntityManagerFactory entityManagerFactory;
@BeforeEach
void setUp() {
entityManagerFactory = JpaUtil.getEntityManagerFactory();
}
}
Di dalam kelas ini, terdapat satu atribut privat bernama entityManagerFactory
yang merupakan instance dari kelas EntityManagerFactory
. EntityManagerFactory
adalah bagian dari Java Persistence API (JPA) yang digunakan untuk mengelola entitas dan koneksi ke basis data.
Kemudian, terdapat sebuah method setUp()
. Method ini dianotasi dengan @BeforeEach
, yang menandakan bahwa akan dijalankan sebelum setiap pengujian (test) dilakukan. Di dalam method setUp()
, instance dari EntityManagerFactory
diinisialisasi menggunakan method JpaUtil.getEntityManagerFactory()
yang sebelumnya sudah ditambahkan. Dapat diasumsikan bahwa method ini mengembalikan instance yang sesuai dari EntityManagerFactory
yang diperlukan untuk melakukan pengujian terhadap operasi-operasi CRUD (Create, Read, Update, Delete) pada basis data.
Lalu, tambahkan method insertData()
untuk menambahkan data
@Test
void insertData() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
Customer customer = new Customer();
customer.setId("1");
customer.setName("Ujang");
customer.setPrimaryEmail("ujang@test.com");
customer.setMarried(true);
Assertions.assertNotNull(customer);
entityManager.persist(customer);
entityTransaction.commit();
entityManager.close();
}
Di dalam method insertData()
, pertama-tama, sebuah objek EntityManager
dibuat dengan menggunakan entityManagerFactory.createEntityManager()
. EntityManager
adalah komponen utama dalam JPA yang digunakan untuk berinteraksi dengan basis data. Setelah itu, objek EntityTransaction
diperoleh dari EntityManager
dengan memanggil entityManager.getTransaction()
. EntityTransaction
digunakan untuk memulai dan mengelola transaksi dengan basis data.
Setelah transaksi dimulai dengan memanggil entityTransaction.begin()
, sebuah objek Customer
dibuat. Atribut-atribut objek Customer
seperti id
, name
, primaryEmail
, dan married
diinisialisasi dengan nilai-nilai tertentu. Selanjutnya, dilakukan pengujian dengan memastikan bahwa objek customer
tidak bernilai null
menggunakan Assertions.assertNotNull(customer)
. Jika objek customer
tidak null
, artinya objek tersebut telah berhasil dibuat dan diinisialisasi dengan nilai-nilai yang benar.
Kemudian, objek customer
disimpan (inserted) ke dalam basis data menggunakan metode entityManager.persist(customer)
. Setelah itu, transaksi diakhiri dengan memanggil entityTransaction.commit()
, yang mengonfirmasi bahwa perubahan yang dilakukan dalam transaksi harus disimpan ke dalam basis data. Terakhir, objek EntityManager
ditutup dengan memanggil entityManager.close()
, yang menutup sesi penggunaan EntityManager
dan mengakhiri koneksi ke basis data. Dengan demikian, metode insertData()
ini menguji operasi insert data ke basis data untuk objek pelanggan (Customer
). Jika operasi ini berhasil tanpa kesalahan, pengujian dianggap berhasil.
Sama halnya dengan method insert, berikut beberapa operasi CRUD yang lain yang dapat Anda coba:
@Test
void findData() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
Customer customer = entityManager.find(Customer.class, "1");
Assertions.assertNotNull(customer);
Assertions.assertEquals("1", customer.getId());
Assertions.assertEquals("Ujang", customer.getName());
entityTransaction.commit();
entityManager.close();
}
@Test
void updateData() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
Customer customer = entityManager.find(Customer.class, "1");
customer.setName("Abdul");
entityManager.merge(customer);
Assertions.assertEquals("1", customer.getId());
Assertions.assertEquals("Abdul", customer.getName());
entityTransaction.commit();
entityManager.close();
}
@Test
void deleteData() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
Customer customer = entityManager.find(Customer.class, "1");
entityManager.remove(customer);
entityTransaction.commit();
entityManager.close();
}
Perbedaannya hanya terletak pada method yg digunakan pada setiap operasi. Pada hibernate, operasi insert menggunakan method persist()
, untuk get data, gunakan method find()
, method merge()
untuk update dan remove()
untuk delete. Namun, pada hibernate diharuskan untuk menggunakan method find()
terlebih dahulu pada operasi update dan delete sebagai validasi apakah data tersedia atau tidak.