Jika sebelumnya pada artikel Integrasi Redis Dalam Spring Boot dengan Redis Template kita menggunakan Redis Template untuk berinteraksi antara Spring dan Redis, maka kali ini kita akan menggunakan Spring Data.
Karena kita akan meneruskan dari artikelsebelumnya aka kita akan mengulas sedikit tentang apa saja yang sebelumnya kita lakukan pada Integrasi Redis Dalam Spring Boot dengan Redis Template di branch "redis" pada repository https://github.com/yoesoff/YBoilerplate.git.
Artikel ini memakai branch redis_spring_data dari branch redis |
Dengan menggunakan Spring Data Redis dapat mempermudah interaksi dengan Redis dengan lebih otomatis. Dengan Spring Data Redis, kita dapat memanfaatkan repository untuk operasi CRUD, yang mengurangi jumlah kode manual dan menjadikan akses data lebih idiomatis dalam Spring Boot. Berikut adalah langkah-langkahnya:
1. Tambahkan Model Entity
Misalkan kita ingin menyimpan objek sederhana seperti DataObject
.
package com.mhyusuf.yboilerplate.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import java.io.Serializable;
@RedisHash("DataObject")
public class DataObject implements Serializable {
@Id
private String id;
private String value;
// Constructors, getters, and setters
public DataObject() {}
public DataObject(String id, String value) {
this.id = id;
this.value = value;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
2. Buat Interface RedisRepository
Pertama, buat repository interface menggunakan Spring Data Redis dengan anotasi @Repository. Anda juga perlu mengatur entitas yang disimpan ke dalam Redis.
Spring Data Redis menyediakan antarmuka CrudRepository
yang dapat Anda gunakan untuk mengakses Redis secara otomatis. Buat repository seperti berikut:
package com.mhyusuf.yboilerplate.repository;
import com.mhyusuf.yboilerplate.entity.DataObject;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DataObjectRepository extends CrudRepository<DataObject, String> {
// Tidak perlu menambahkan metode apapun, cukup menggunakan metode CRUD dasar
}
3. Update RedisService
untuk Menggunakan DataObjectRepository
Dengan repository, Anda bisa memodifikasi RedisService
untuk menggunakan DataObjectRepository
dalam operasi CRUD.
"Opsi lain juga bisa dengan tidak perlu merubah apapun pada RedisService tapi dengan membut service baru bernama RedisSpringDataService."
package com.mhyusuf.yboilerplate.service;
import com.mhyusuf.yboilerplate.entity.DataObject;
import com.mhyusuf.yboilerplate.repository.DataObjectRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RedisSpringDataService {
private final DataObjectRepository dataObjectRepository;
@Autowired
public RedisSpringDataService(DataObjectRepository dataObjectRepository) {
this.dataObjectRepository = dataObjectRepository;
}
public void saveData(String key, String value) {
dataObjectRepository.save(new DataObject(key, value));
}
public String getData(String key) {
return dataObjectRepository.findById(key)
.map(DataObject::getValue)
.orElse(null);
}
public boolean deleteData(String key) {
if (dataObjectRepository.existsById(key)) {
dataObjectRepository.deleteById(key);
return true;
}
return false;
}
public Iterable<DataObject> getAllData() {
return dataObjectRepository.findAll();
}
}
Selanjutnya kita akan gunakan service yang bernama RedisSpringDataService.
5. Update RedisSpringDataController
Sesuaikan RedisSpringDataController
untuk menggunakan metode baru di RedisSpringDataService
:
"Saya kali ini tidak mengubah RedisController tapi membuat Controller baru bernama RedisSpringDataController"
package com.mhyusuf.yboilerplate.controller;
import com.mhyusuf.yboilerplate.entity.DataObject;
import com.mhyusuf.yboilerplate.service.RedisSpringDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/redisspringdata")
public class RedisSpringDataController {
private final RedisSpringDataService redisService;
@Autowired
public RedisSpringDataController(RedisSpringDataService redisService) {
this.redisService = redisService;
}
@PostMapping("/{key}")
public ResponseEntity<String> createOrUpdateData(@PathVariable String key, @RequestBody String value) {
redisService.saveData(key, value);
return ResponseEntity.ok("Data saved successfully");
}
@GetMapping("/{key}")
public ResponseEntity<String> getData(@PathVariable String key) {
String value = redisService.getData(key);
if (value != null) {
return ResponseEntity.ok(value);
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Data not found");
}
}
@DeleteMapping("/{key}")
public ResponseEntity<String> deleteData(@PathVariable String key) {
if (redisService.deleteData(key)) {
return ResponseEntity.ok("Data deleted successfully");
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Data not found");
}
}
@GetMapping("/all")
public ResponseEntity<Iterable<DataObject>> getAllData() {
return ResponseEntity.ok(redisService.getAllData());
}
}
6. Tambahkan Konfigurasi Spring Data Redis di application.properties
Cek kembali konfigurasi Redis tambahkan jika belum ada atau sesuaikan dengan setup redis anda:
spring.data.redis.host=localhost
spring.data.redis.port=6379
7. Unit Testing
Terakhir, perbarui unit test di RedisControllerTest
untuk menguji antarmuka baru berbasis RedisRepository
.
"Atau opsi lain adalah dengan membuat Unit Test Baru supaya test lama masih bisa buat belajar, toh service dan controller lama tidak kita hapus maupun ubah"
7.1. RedisSpringDataControllerTest
package com.mhyusuf.yboilerplate;
import com.mhyusuf.yboilerplate.controller.RedisSpringDataController;
import com.mhyusuf.yboilerplate.entity.DataObject;
import com.mhyusuf.yboilerplate.service.RedisSpringDataService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collections;
import java.util.Set;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(RedisSpringDataController.class)
class RedisSpringDataControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private RedisSpringDataService redisService; // Menggunakan @MockBean untuk memock RedisService
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testCreateOrUpdateData() throws Exception {
String key = "testKey";
String value = "testValue";
doNothing().when(redisService).saveData(key, value);
mockMvc.perform(post("/api/redisspringdata/{key}", key)
.content(value)
.contentType(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Data saved successfully"));
}
@Test
void testGetData() throws Exception {
String key = "testKey";
String value = "testValue";
when(redisService.getData(key)).thenReturn(value);
mockMvc.perform(get("/api/redisspringdata/{key}", key))
.andExpect(status().isOk())
.andExpect(content().string(value));
}
@Test
void testGetData_NotFound() throws Exception {
String key = "unknownKey";
when(redisService.getData(key)).thenReturn(null);
mockMvc.perform(get("/api/redisspringdata/{key}", key))
.andExpect(status().isNotFound())
.andExpect(content().string("Data not found"));
}
@Test
void testDeleteData() throws Exception {
String key = "testKey";
when(redisService.deleteData(key)).thenReturn(true);
mockMvc.perform(delete("/api/redisspringdata/{key}", key))
.andExpect(status().isOk())
.andExpect(content().string("Data deleted successfully"));
}
@Test
void testDeleteData_NotFound() throws Exception {
String key = "unknownKey";
when(redisService.deleteData(key)).thenReturn(false);
mockMvc.perform(delete("/api/redisspringdata/{key}", key))
.andExpect(status().isNotFound())
.andExpect(content().string("Data not found"));
}
@Test
void testGetAllData() throws Exception {
// Buat objek DataObject dengan key dan value
DataObject dataObject = new DataObject("testKey", "testValue");
Set<DataObject> dataObjects = Collections.singleton(dataObject);
// Mock service untuk mengembalikan dataObjects
when(redisService.getAllData()).thenReturn(dataObjects);
// Lakukan perform GET request dan validasi respons JSON
mockMvc.perform(get("/api/redisspringdata/all"))
.andExpect(status().isOk());
}
}
7.2 RedisSpringDataServiceTest
package com.mhyusuf.yboilerplate;
import com.mhyusuf.yboilerplate.entity.DataObject;
import com.mhyusuf.yboilerplate.repository.DataObjectRepository;
import com.mhyusuf.yboilerplate.service.RedisSpringDataService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
import java.util.stream.StreamSupport;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class RedisSpringDataServiceTest {
@Mock
private DataObjectRepository dataObjectRepository;
@InjectMocks
private RedisSpringDataService redisSpringDataService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testSaveData() {
// Arrange
ArgumentCaptor<DataObject> captor = ArgumentCaptor.forClass(DataObject.class);
DataObject expectedDataObject = new DataObject("testKey", "testValue");
// Act
redisSpringDataService.saveData("testKey", "testValue");
// Assert
verify(dataObjectRepository, times(1)).save(captor.capture());
DataObject actualDataObject = captor.getValue();
assertEquals(expectedDataObject.getId(), actualDataObject.getId());
assertEquals(expectedDataObject.getValue(), actualDataObject.getValue());
}
@Test
void testGetData() {
String key = "testKey";
DataObject dataObject = new DataObject(key, "testValue");
when(dataObjectRepository.findById(key)).thenReturn(Optional.of(dataObject));
String result = redisSpringDataService.getData(key);
assertNotNull(result);
assertEquals("testValue", result);
verify(dataObjectRepository, times(1)).findById(key);
}
@Test
void testGetDataNotFound() {
String key = "nonExistentKey";
when(dataObjectRepository.findById(key)).thenReturn(Optional.empty());
String result = redisSpringDataService.getData(key);
assertNull(result);
verify(dataObjectRepository, times(1)).findById(key);
}
@Test
void testDeleteData() {
String key = "testKey";
when(dataObjectRepository.existsById(key)).thenReturn(true);
boolean isDeleted = redisSpringDataService.deleteData(key);
assertTrue(isDeleted);
verify(dataObjectRepository, times(1)).deleteById(key);
}
@Test
void testDeleteDataNotFound() {
String key = "nonExistentKey";
when(dataObjectRepository.existsById(key)).thenReturn(false);
boolean isDeleted = redisSpringDataService.deleteData(key);
assertFalse(isDeleted);
verify(dataObjectRepository, times(0)).deleteById(key);
}
@Test
void testGetAllData() {
Iterable<DataObject> dataObjects = List.of(
new DataObject("key1", "value1"),
new DataObject("key2", "value2")
);
when(dataObjectRepository.findAll()).thenReturn(dataObjects);
Iterable<DataObject> result = redisSpringDataService.getAllData();
assertNotNull(result);
assertEquals(2, StreamSupport.stream(result.spliterator(), false).count());
verify(dataObjectRepository, times(1)).findAll();
}
}
Setelah langkah-langkah ini, aplikasi Spring Boot Anda sudah siap untuk menggunakan Redis melalui Spring Data, memungkinkan pengelolaan data yang lebih mudah dan sesuai dengan pola standar Spring Boot.
Terkait Repository dan Kode Ini
Files yang di tambahkan selama artikel diatas dibuat. |
Semua kode di atas sudah di push ke Repo |
Tambahan Informasi
Pertanyaan:
Apakah dengan menggunakan Spring Data untuk Redis kali ini kita juga menggunakan JPA (Spesifikasi Java Persistant API)?
Jawab:
Sebaliknya, Spring Data Redis adalah modul Spring khusus untuk bekerja dengan database key-value store Redis. Redis tidak memiliki model relasional seperti database SQL, jadi pendekatan dan API yang digunakan berbeda dengan JPA. Spring Data Redis menyediakan API khusus yang cocok untuk operasi berbasis key-value, seperti menyimpan, mengambil, dan menghapus data dari Redis.
Berikut adalah beberapa perbedaan utama:
Model Data:
- JPA: Menggunakan entitas yang dipetakan ke tabel-tabel di database relasional.
- Spring Data Redis: Beroperasi dengan struktur data Redis seperti string, hashes, lists, sets, dan sorted sets.
Repository:
- JPA: Menggunakan
JpaRepository
atauCrudRepository
untuk operasi database. - Spring Data Redis: Menggunakan
RedisRepository
atau langsung berinteraksi denganRedisTemplate
.
- JPA: Menggunakan
Fitur Spesifik:
- JPA: Mendukung kueri yang kompleks, relasi antar-entitas, dan transaksi database.
- Spring Data Redis: Fokus pada penyimpanan data cepat dan cache, tanpa dukungan untuk relasi dan transaksi seperti JPA.
Jadi, jika Anda menggunakan Spring Data Redis, Anda tidak menggunakan spesifikasi JPA karena Redis bukan database relasional dan memiliki cara kerja yang berbeda.
Comments
Post a Comment