SpringBoot에서 Redis를 데이터베이스로 사용하기 Using Redis as Database in SpringBoot
- Dessert shop example using SpringBoot and Redis
- Let’s create a dessert shop example to use Redis
Stack
-
SpringBoot
- Lombok
- Spring Reactive Web
- Spring Data Reactive Redis
-
Validation
- Gradle
- Java 11 +
- Redis
Starting the Redis Server
- Redis server must be running
- Create embedded locally, create with Docker locally, or use Redis installed in the cloud
-
Installing using docker-compose
-
version: "3" services: redis-docker: image: redis:latest command: redis-server --requirepass qwerqwer123 --port 6379 container_name: "docker-redis" labels: - "name=redis" - "mode=standalone" volumes: - /Users/wool/Database-docker/data/redis:/data ports: - 6379:6379 - If you don’t want to apply a password to Redis, omit the
--requirepassoption - Start Redis server
$ docker-compose -f docker-compose.redis.yml up -d
Writing Configuration
- ReactiveRedisConfiguration.java
- Write configuration file to connect Redis and Spring
-
Create
reactRedisConnectionFactorybean to create a Redis connection factory@Bean public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() { return new LettuceConnectionFactory( Objects.requireNonNull(env.getProperty("spring.redis.host")), Integer.parseInt(Objects.requireNonNull((env.getProperty("spring.redis.port")))) ); }Alternatively, you can use the
@Valueannotation to organize the data cleanly as follows@Value("${spring.redis.host}") private String redisHost; @Value("${spring.redis.port}") private int redisPort; @Bean public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() { return new LettuceConnectionFactory(redisHost, redisPort); } -
Since we need to send and receive Redis data, we set up an Operation Bean using the Jackson library that supports JSON format.
@Bean public ReactiveRedisOperations<String, Object> redisOperations(ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) { Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder = RedisSerializationContext.newSerializationContext(new StringRedisSerializer()); RedisSerializationContext<String, Object> context = builder.value(serializer).hashValue(serializer) .hashKey(serializer).build(); return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, context); }
Writing CRUD API
- A model class is needed first to write CRUD
Writing Domain Model
-
Creating domain objects
-
Create Dessert domain object with pk, product name, category, description, price, and release date
Writing Repository to Access Database
- Write a Repository to connect to the Redis database
- Specify CRUD functions using Interface and implement internal logic using implementation
- Create ObjectMapperUtils class for JSON parsing
-
Create ReactiveRedisComponent class for Redis connection
-
public interface DessertRepository { Mono<Dessert> save(Dessert dessert); Mono<Dessert> get(String key); Flux<Dessert> getAll(); Mono<Long> delete(String id); } -
public class RedisDessertRepository implements DessertRepository { private ReactiveRedisOperations<String, Object> redisOperations; public RedisDessertRepository(ReactiveRedisOperations<String, Object> redisOperations) { this.redisOperations = redisOperations; } @Override public Mono<Dessert> save(Dessert dessert) { return redisOperations.opsForValue().set(dessert.getId(), dessert); } @Override public Mono<Dessert> get(String key) { return redisOperations.opsForValue().get(key); } @Override public Flux<Dessert> getAll() { return redisOperations.keys("*") .flatMap(redisOperations::opsForValue::get) .cast(Dessert.class); } @Override public Mono<Long> delete(String id) { return redisOperations.delete(id); } }
Writing Service
- First specify the Interface, then implement with the implementation class
-
DessertService
-
public interface DessertService { Mono<Dessert> create(Dessert dessert); Flux<Dessert> getAll(); Mono<Dessert> getOne(String id); Mono<Long> deleteById(String id); } -
@RequiredArgsConstructor @Service public class DessertServiceImpl implements DessertService { private final RedisDessertRepository redisDessertRepository; @Override public Mono<Dessert> create(Dessert dessert) { return redisDessertRepository.save(dessert); } @Override public Flux<Dessert> getAll() { return redisDessertRepository.getAll(); } @Override public Mono<Dessert> getOne(String id) { return redisDessertRepository.get(id); } @Override public Mono<Long> deleteById(String id) { return redisDessertRepository.delete(id); } }
Creating Controller
-
Create DessertController to handle and respond to API calls
@RestController @RequestMapping("/v1") @RequiredArgsConstructor public class DessertController { private final DessertServiceImpl dessertService; @PostMapping("/dessert") @ResponseStatus(HttpStatus.CREATED) public Mono<Dessert> addDessert(@RequestBody @Valid Dessert dessert) { return dessertService.create(dessert); } @GetMapping("/dessert") public Flux<Dessert> getAllDessert() { return dessertService.getAll(); } @GetMapping("/dessert/{id}") public Mono<Dessert> getDessert(@PathVariable String id) { return dessertService.getOne(id); } @DeleteMapping("/dessert/{id}") public Mono<Long> deleteDessert(@PathVariable String id) { return dessertService.deleteById(id); } }
댓글남기기