Skip to content

Commit

Permalink
[TDC-35] RedisTemplate 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
yerimkoko committed Jan 8, 2024
1 parent 6e1196b commit 9d54868
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.threedollar.domain.options.PollOption;
import com.threedollar.domain.poll.Poll;
import com.threedollar.domain.poll.PollCategory;
import com.threedollar.service.options.dto.request.PollOptionCreateRequest;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.threedollar.service.options.dto.request;
package com.threedollar.service.poll.dto.request;

import com.threedollar.domain.options.PollOption;
import com.threedollar.domain.poll.Poll;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.threedollar.domain.AccountType;
import com.threedollar.domain.options.PollOption;
import com.threedollar.domain.poll.Poll;
import com.threedollar.service.options.dto.response.OptionsResponse;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.threedollar.service.options.dto.response;
package com.threedollar.service.poll.dto.response;

import com.threedollar.domain.options.PollOption;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.threedollar.common.exception.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS;
import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true)
.configure(FAIL_ON_EMPTY_BEANS, false);

public static <T> T toObject(String input, Class<T> toClass) {
try {
return OBJECT_MAPPER.readValue(input, toClass);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("역직렬화 중 에러가 발생하였습니다. input: (%s) toClass: (%s) message: (%s)", input, toClass.getSimpleName(), e.getMessage()));
}
}

public static <T> String toJson(T input) {
try {
return OBJECT_MAPPER.writeValueAsString(input);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(String.format("직렬화 중 에러가 발생하였습니다. input: (%s) message: (%s)", input, e.getMessage()));
}
}

public static <T> List<T> toList(String json, Class<T> clazz) {
try {
JavaType listType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
ObjectReader reader = OBJECT_MAPPER.readerFor(listType);
List<T> listValue = reader.readValue(toJsonNode(json));
return listValue == null ? List.of() : listValue;
} catch (IOException e) {
throw new IllegalArgumentException(String.format("List 직렬화 중 에러가 발생하였습니다. input: <%s> message: <%s>", json, e.getMessage()));
}
}

public static <T> List<T> toList(JsonNode jsonNode, Class<T> clazz) {
try {
JavaType listType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
ObjectReader reader = OBJECT_MAPPER.readerFor(listType);
List<T> listValue = reader.readValue(jsonNode);
return listValue == null ? List.of() : listValue;
} catch (IOException e) {
throw new IllegalArgumentException(String.format("List 직렬화 중 에러가 발생하였습니다. input: <%s> message: <%s>", jsonNode, e.getMessage()));
}
}

public static JsonNode toJsonNode(String json) {
try {
return OBJECT_MAPPER.readTree(json);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("JsonNode 직렬화 중 에러가 발생하였습니다. input: <%s> message: <%s>", json, e.getMessage()));
}
}

public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class Reaction extends BaseEntity {
private String accountId;

@Column(nullable = false)
private String targetId; // poll, review 등 ..
private String targetId; // pollId, reviewId 등 ..


@Builder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.threedollar.domain.redis;

import java.time.Duration;

public interface StringRedisKey<K, V> {

String getKey();

V deserializeValue(String value);

String serializeValue(V value);

Duration getTtl();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.threedollar.domain.redis;

import java.time.Duration;
import java.util.List;
import java.util.Map;

public interface StringRedisRepository<K extends StringRedisKey<K, V>,V> {

V get(K k);

List<V> getBulk(List<K> keys);

void set(K k, V v);

void setBulk(Map<K, V> keyValues);

void setWithTtl(K k, V v, Duration ttl);

void incr(K k);

void incrBulk(List<K> keys);

void incrBy(K k, long value);

void decr(K k);

void decrBulk(List<K> keys);

void decrBy(K k, long value);

void del(K k);

void delBulk(List<K> keys);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.threedollar.domain.redis;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Repository
public class StringRedisRepositoryImpl<K extends StringRedisKey<K, V>, V> implements StringRedisRepository<K, V>{

private final RedisTemplate<String, String> redisTemplate;

@Override
public V get(K k) {
ValueOperations<String, String> operations = redisTemplate.opsForValue();
return k.deserializeValue(operations.get(k.getKey()));
}

@Override
public List<V> getBulk(List<K> keys) {
if (keys.isEmpty()) {
return new ArrayList<>();
}
K k = keys.get(0);

ValueOperations<String, String> operations = redisTemplate.opsForValue();

List<String> values = operations.multiGet(keys.stream()
.map(K::getKey)
.collect(Collectors.toList())
);

if (values == null) {
return Collections.emptyList();
}

return values.stream()
.map(k::deserializeValue)
.collect(Collectors.toList());
}

@Override
public void set(K k, V v) {
setWithTtl(k, v, k.getTtl());

}

@Override
public void setBulk(Map<K, V> keyValues) {
ValueOperations<String, String> operations = redisTemplate.opsForValue();
redisTemplate.executePipelined((RedisCallback<Object>) pipeline -> {
keyValues.forEach((k, v) -> operations.set(k.getKey(), String.valueOf(v)));
return null;
});
}

@Override
public void setWithTtl(K k, V v, Duration ttl) {
ValueOperations<String, String> operations = redisTemplate.opsForValue();
if (ttl == null) {
operations.set(k.getKey(), k.serializeValue(v));
return;
}
operations.set(k.getKey(), k.serializeValue(v), ttl.getSeconds(), TimeUnit.SECONDS);
}

@Override
public void incr(K k) {
incrBy(k, 1);
}

@Override
public void incrBulk(List<K> keys) {
redisTemplate.executePipelined((RedisCallback<Object>) pipeline -> {
keys.forEach(k -> pipeline.incr(k.getKey().getBytes(StandardCharsets.UTF_8)));
return null;
});
}

@Override
public void incrBy(K k, long value) {
ValueOperations<String, String> operations = redisTemplate.opsForValue();
operations.increment(k.getKey(), value);

}

@Override
public void decr(K k) {
decrBy(k, 1);

}

@Override
public void decrBulk(List<K> keys) {
redisTemplate.executePipelined((RedisCallback<Object>) pipeline -> {
keys.forEach(k -> pipeline.decr(k.getKey().getBytes(StandardCharsets.UTF_8)));
return null;
});
}

@Override
public void decrBy(K k, long value) {
ValueOperations<String, String> operations = redisTemplate.opsForValue();
operations.decrement(k.getKey(), value);
}

@Override
public void del(K k) {
redisTemplate.delete(k.getKey());
}

@Override
public void delBulk(List<K> keys) {
Set<String> keyStrings = keys.stream()
.map(K::getKey)
.collect(Collectors.toSet());
redisTemplate.delete(keyStrings);
}
}

0 comments on commit 9d54868

Please sign in to comment.