Serial Spring Boot: CRUD Dengan Spring Data JPA

Serial Spring Boot: CRUD Dengan Spring Data JPA

Dalam men-develop sebuah aplikasi, semua developer sudah hampir pasti menyentuh bagian CRUD (Create Read Update Delete). CRUD menjadi bagian esensial dari fungsional sebuah aplikasi. Jika kita membuat aplikasi, sudah pasti ada CRUD-nya, meskipun datasource yang digunakan bisa saja bermacam-macam. Ada yang CRUD-nya direct ke database, atau ke store file, atau bahkan via REST API.

Pada tulisan kali ini, saya akan mencoba memberikan gambaran dan tutorial singkat bagaimana melakukan CRUD secara sederhana di Spring Boot melalui Spring Data JPA.

 

 

Baca Juga: Berkenalan Dengan Spring Boot

Pendahuluan

Apa itu Spring Data JPA? Kadang ada kekeliruan pemahaman antara Hibernate dan JPA. JPA atau Java Persistence API sendiri adalah sebuah standar spesifikasi tentang bagaimana cara mengakses database di Java. Jaman old, kita biasanya mengakses database menggunakan plain JDBC, yang mana itu sangat merepotkan sekali. Kita harus membuat koneksi database, memastikan koneksi aman dengan memasang exception, membuat query secara manual dengan Statement atau PreparedStatement, membuat semua spesifikasi from scratch. Dengan menggunakan JPA, kita hanya perlu fokus pada class yang sudah dipetakan ke tabel-tabel di database. Jadi tidak perlu repot mengurusi layer database.

Dan apa itu Hibernate? Hibernate sendiri adalah implementasi dari JPA. JPA menggunakan teknologi ORM atau Object Relational Mapping, yang diimplementasikan oleh framework Hibernate. Ada sebuah diskusi menarik di StackOverflow tentang JPA vs Hibernate ini.

Kembali ke Spring Data JPA. Spring Data JPA sendiri adalah abstraksi dari data access layer. Di dalam Spring Data JPA ini tersedia beberapa macam repository yang dirakit dengan fungsi CRUD dasar untuk memudahkan kita membangun aplikasi. 

Yuk, Mulai Ngoding!

Nah, sekarang kita akan mencoba mengimplementasikan Spring Data JPA untuk melakukan operasi CRUD sederhana. Kita mulai dengan generate project Spring Boot di Spring Initializr.

Untuk dependensi, cukup tambahkan 3 saja: Web, JPA dan MySQL.

Generate Project, extract project yang sudah di-download dan import ke editor kesayangan kalian. Saya pakai Eclipse di sini. Lalu lakukan mvn install.

Sambil menunggu instalasi dependensi selesai (karena internet saya agak lemot, hehe) kita buat dulu database baru. Namanya demo_spring_jpa. Database yang saya gunakan adalah MySQL.

Setelah selesai install dependensi yang dibutuhkan, saatnya sekarang menata struktur project kita. Begini kira-kira struktur project-nya.

 

 

 

 

 

 

 

 

 

 

 

 

Lalu kita set-up file application.properties.

# hibernate
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

# mysql datasoure
spring.datasource.url=jdbc:mysql://localhost:3306/demo_spring_jpa
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root

Mari kita analisa.

  • spring.jpa.hibernate.ddl-auto=create dengan otomatis akan melakukan create table baru pada database kita setiap kali start-up aplikasi.
  • spring.jpa.show-sql=true akan menampilkan query di log output aplikasi.
  • spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true digunakan untuk menghindari LazyInitializationException.
  • spring.datasource.url=jdbc:mysql://localhost:3306/demo_spring_jpa mendefinisikan URL database kita.
  • spring.datasource.driver-class-name=com.mysql.jdbc.Driver mendefinisikan driver database yang kita gunakan. Karena database-nya MySQL, maka driver-nya pun juga punya MySQL.
  • spring.datasource.username=root credential untuk MySQL

 

AppUser.java

package id.daengweb.demo.springdatajpa.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity @Table(name="app_user")
public class AppUser {
	
	@Id @GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	@Column(name="username")
	private String username;
	@Column(name="first_name")
	private String firstName;
	@Column(name="last_name")
	private String lastName;
	@Column(name="join_date")
	private Date joinDate;
	
	@Override
	public String toString() {
		return String.format(""
				+ "AppUser[id=%d,username=%s,firstName=%s,lastName=%s,joinDate=%tD", 
				id, username, firstName, lastName, joinDate);
	}
	
	// setter & getter . . .

}

Anotasi @Entity menandakan kalau class ini adalah JPA entity, yang merupakan abstraksi dari table. Anotasi @Table(name="app_user") menjelaskan kalau class ini di-mapping ke table app_user.

Setiap @Entity harus mempunyai @Id sebagai primary key. Dan itu di-set ke properti id. Selain itu kita juga mendefinisikan Generation Strategy untuk id, yaitu auto increment. Sedangkan anotasi @Column dengan parameter name digunakan untuk mendefinisikan alias dari property. Misal, property firstName mempunyai alias first_name di database.

 

AppUserRepository.java

package id.daengweb.demo.springdatajpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import id.daengweb.demo.springdatajpa.model.AppUser;

public interface AppUserRepository extends JpaRepository<AppUser, Integer> {

}

Interface ini berlaku sebagai repostitory JPA. Dengan meng-extends JpaRepository, kita bisa menggunakan method built-in dari JPA, misalnya save(), getOne() dan lain-lain (akan kita coba nanti). Selain itu, kita juga memberikan parameter AppUser yang melakukan mapping class AppUser yang memiliki id dengan tipe Integer ke repository ini. Jadi, ketika kita memanggil method save(args0) nanti, kita akan menyimpan object dengan tipe AppUser ke database.

 

AppController.java

package id.daengweb.demo.springdatajpa.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class AppController {
	
	@RequestMapping("/home")
	public ModelAndView home() {
		ModelAndView mv = new ModelAndView("home");
		return mv;
	}

}

Class ini berfungsi sebagai controller jika kita ingin mengakses aplikasi via web.

 

Application.java

package id.daengweb.demo.springdatajpa;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import id.daengweb.demo.springdatajpa.model.AppUser;
import id.daengweb.demo.springdatajpa.repository.AppUserRepository;

@SpringBootApplication
public class Application implements CommandLineRunner {
	
	@Autowired
	private AppUserRepository appUserRepository;

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		
		System.out.println("Creating AppUser object: appUser1");
		AppUser appUser1 = new AppUser();
		appUser1.setUsername("appuser1");
		appUser1.setFirstName("Richard");
		appUser1.setLastName("Stallman");
		appUser1.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("Creating AppUser object: appUser2");
		AppUser appUser2 = new AppUser();
		appUser2.setUsername("appuser2");
		appUser2.setFirstName("Linus");
		appUser2.setLastName("Torvalds");
		appUser2.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("saving appUser1 . . .");
		this.appUserRepository.save(appUser1);
		System.out.println("appUser1 saved.");
		
		System.out.println("saving appUser2 . . .");
		this.appUserRepository.save(appUser2);
		System.out.println("appUser2 saved.");
		
	}
}

Di sini kita akan mencoba mengetes kodenya. Kita membuat object dari AppUserRepository. Dan kita juga meng-implement CommandLineRunner supaya hasilnya bisa dilihat di log output

 

Test The Code

Jalankan file Application.java. Kita akan mendapatkan hasil sepert ini.

Sekarang coba cek database kita.

Yups, datanya sudah masuk.

Jadi, bisa kita lihat, kita tidak perlu capek-capek membuat method untuk save, sudah disediakan oleh Spring Data JPA. Sekarang, bagaimana jika kita ingin mengambil data?

Modifikasi sedikit file Application.java menjadi seperti ini.

package id.daengweb.demo.springdatajpa;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import id.daengweb.demo.springdatajpa.model.AppUser;
import id.daengweb.demo.springdatajpa.repository.AppUserRepository;

@SpringBootApplication
public class Application implements CommandLineRunner {
	
	@Autowired
	private AppUserRepository appUserRepository;

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		
		System.out.println("Creating AppUser object: appUser1");
		AppUser appUser1 = new AppUser();
		appUser1.setUsername("appuser1");
		appUser1.setFirstName("Richard");
		appUser1.setLastName("Stallman");
		appUser1.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("Creating AppUser object: appUser2");
		AppUser appUser2 = new AppUser();
		appUser2.setUsername("appuser2");
		appUser2.setFirstName("Linus");
		appUser2.setLastName("Torvalds");
		appUser2.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("saving appUser1 . . .");
		this.appUserRepository.save(appUser1);
		System.out.println("appUser1 saved.");
		
		System.out.println("saving appUser2 . . .");
		this.appUserRepository.save(appUser2);
		System.out.println("appUser2 saved.");
		
		System.out.println("Getting users data . . . ");
		List<AppUser> users = this.appUserRepository.findAll();
		System.out.println(String.format("Found %d users!", users.size()));
		
		System.out.println("Displaying users . . . ");
		for(AppUser user : users) {
			System.out.println(user);
		}
		
	}
}

Lalu run lagi file Application.java, dan akan kita dapatkan hasil seperti ini.

Yuhu, mudah kan?

Lalu bagaimana dengan update dan delete? Modifikasi lagi method run di file Application.java.

package id.daengweb.demo.springdatajpa;

// imports . . .

@SpringBootApplication
public class Application implements CommandLineRunner {
	
	// declaring appUserRepository & main method

	@Override
	public void run(String... args) throws Exception {
		
		System.out.println("Creating AppUser object: appUser1");
		AppUser appUser1 = new AppUser();
		appUser1.setUsername("appuser1");
		appUser1.setFirstName("Richard");
		appUser1.setLastName("Stallman");
		appUser1.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("Creating AppUser object: appUser2");
		AppUser appUser2 = new AppUser();
		appUser2.setUsername("appuser2");
		appUser2.setFirstName("Linus");
		appUser2.setLastName("Torvalds");
		appUser2.setJoinDate(new Date());
		System.out.println("appUser1 created.");
		
		System.out.println("saving appUser1 . . .");
		this.appUserRepository.save(appUser1);
		System.out.println("appUser1 saved.");
		
		System.out.println("saving appUser2 . . .");
		this.appUserRepository.save(appUser2);
		System.out.println("appUser2 saved.");
		
		System.out.println("\nGetting users data . . . ");	
		List<AppUser> users = this.appUserRepository.findAll();
		System.out.println(String.format("Found %d user/users!", users.size()));
		
		System.out.println("\nGet user with id=1");
		AppUser userFromDb1 = this.appUserRepository.getOne(1);
		if(userFromDb1 != null) {
			System.out.println("Found user with id=1 !");
			userFromDb1.setFirstName("James");
			userFromDb1.setLastName("Gosling");
			
			System.out.println("Updating user with id=1 . . .");
			this.appUserRepository.save(userFromDb1);
			System.out.println("User updated.");
		}
		
		System.out.println("Get user with id=2");
		AppUser userFromDb2 = this.appUserRepository.getOne(2);
		if(userFromDb2 != null) {
			System.out.println("Found user with id=2 !");
			System.out.println("Deleting user with id=2 . . .");
			this.appUserRepository.delete(userFromDb2);
			System.out.println("User deleted.");
		}
		
		System.out.println("\nGetting users data . . . ");	
		users = this.appUserRepository.findAll();
		System.out.println(String.format("Found %d user/users!", users.size()));
		
		System.out.println("Displaying users . . .");
		for(AppUser user : users) {
			System.out.println(user);
		}
		
	}
}

Dan coba jalankan lagi file Application.java.

Baca Juga: Berkenalan Dengan Automated Testing

Penutup

Demikian tulisan saya tentang CRUD sederhana dengan Spring Data JPA. Waktu kita akan lebih banyak dihemat karena tidak perlu membuat operasi-operasi dasar CRUD dari suatu table. Bagaimana jika CRUD kita lebih advance? Misal, kita ingin mengambil data user hanya menggunakan nama depan, atau nama belakang, atau menggunakan klausa LIKE pada nama depan? Atau kita ingin mengambil data user berdasarkan tanggal registrasi, umur atau kota tempat tinggal?

Semua itu bisa di atasi dengan membuat custom query. Kita akan membahasnya di serial Spring Boot berikutnya.

Happy Coding! Cheers!

Category:
Share:

Comments