Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.20</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>net.datafaker</groupId>
<artifactId>datafaker</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>

</project>
45 changes: 44 additions & 1 deletion src/main/java/org/redis/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
package org.redis;

import lombok.extern.slf4j.Slf4j;
import org.redis.model.Student;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;

@Slf4j
public class Main {
private static final String HOST = "localhost";
private static final int PORT = 6379;
private static final String REDIS_KEY = "students";


public static void main(String[] args) {
System.out.println("Hello world!");

RedisMap<String, Student> map = getStringStudentRedisMap();

log.info("user1: {}", map.get("user1"));
log.info("user2: {}", map.get("user2"));

log.info("Map size: {}", map.size());
log.info("Keys: {}", map.keySet());
log.info("Values: {}", map.values());

map.forEach((key, value) -> log.info("Entry: key={}, value={}", key, value));
}

private static RedisMap<String, Student> getStringStudentRedisMap() {
JedisPool pool = new JedisPool(new JedisPoolConfig(), HOST, PORT);

RedisMap<String, Student> map = new RedisMap<>(
pool,
REDIS_KEY,
String.class,
Student.class
);

map.clear();

Student s1 = new Student("Sergey", 20, List.of("Java", "Spring", "Redis"));
Student s2 = new Student("Andrew", 22, List.of("Python", "Django"));

map.put("user1", s1);
map.put("user2", s2);
return map;
}
}
190 changes: 190 additions & 0 deletions src/main/java/org/redis/RedisMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package org.redis;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;

import java.util.AbstractMap;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class RedisMap<K, V> implements Map<K, V> {
private static final String KEY_IS_NULL_STRING = "Key is null";

private final JedisPool pool;
private final String redisKey;
private final ObjectMapper mapper;
private final Class<K> keyClass;
private final Class<V> valueClass;

public RedisMap(JedisPool pool, String redisKey, Class<K> keyClass, Class<V> valueClass) {
this.pool = pool;
this.redisKey = redisKey;
this.keyClass = keyClass;
this.valueClass = valueClass;
this.mapper = new ObjectMapper();
}

private String serialize(Object value) {
try {
return mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
log.error(e.getMessage());
throw new IllegalStateException("JSON serialization failed: " + value, e);
}
}

private <T> T deserialize(String json, Class<T> type) {
try {
return mapper.readValue(json, type);
} catch (Exception e) {
log.error(e.getMessage());
throw new IllegalStateException("JSON deserialization failed: " + json, e);
}
}

@Override
public int size() {
try (Jedis jedis = pool.getResource()) {
return Math.toIntExact(jedis.hlen(redisKey));
}
}

@Override
public boolean isEmpty() {
return size() == 0;
}

@Override
public boolean containsKey(Object key) {
Objects.requireNonNull(key, KEY_IS_NULL_STRING);

try (Jedis jedis = pool.getResource()) {
return jedis.hexists(redisKey, serialize(key));
}
}

@Override
public boolean containsValue(Object value) {
if (!valueClass.isInstance(value)) {
return false;
}

try (Jedis jedis = pool.getResource()) {
String cursor = ScanParams.SCAN_POINTER_START;
ScanParams params = new ScanParams().count(100);

do {
ScanResult<Map.Entry<String, String>> scanResult = jedis.hscan(redisKey, cursor, params);

for (Map.Entry<String, String> entry : scanResult.getResult()) {
V val = deserialize(entry.getValue(), valueClass);
if (val.equals(value)) {
return true;
}
}

cursor = scanResult.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
}

return false;
}

@Override
public V get(Object key) {
Objects.requireNonNull(key, KEY_IS_NULL_STRING);

try (Jedis jedis = pool.getResource()) {
String json = jedis.hget(redisKey, serialize(key));
return json != null ? deserialize(json, valueClass) : null;
}
}

@Override
public V put(K key, V value) {
Objects.requireNonNull(key, KEY_IS_NULL_STRING);
Objects.requireNonNull(value, "Value is null");

V old = get(key);
try (Jedis jedis = pool.getResource()) {
jedis.hset(redisKey, serialize(key), serialize(value));
}
return old;
}

@Override
public V remove(Object key) {
Objects.requireNonNull(key, KEY_IS_NULL_STRING);

V old = get(key);
try (Jedis jedis = pool.getResource()) {
jedis.hdel(redisKey, serialize(key));
}
return old;
}

@Override
public void putAll(Map<? extends K, ? extends V> m) {
Objects.requireNonNull(m, "Map is null");

Map<String, String> map = m.entrySet()
.stream()
.collect(Collectors.toMap(
e -> serialize(e.getKey()),
e -> serialize(e.getValue())
));

try (Jedis jedis = pool.getResource()) {
jedis.hset(redisKey, map);
}
}

@Override
public void clear() {
try (Jedis jedis = pool.getResource()) {
jedis.del(redisKey);
}
}

@Override
public Set<K> keySet() {
try (Jedis jedis = pool.getResource()) {
return jedis.hkeys(redisKey)
.stream()
.map(k -> deserialize(k, keyClass))
.collect(Collectors.toSet());
}
}

@Override
public Collection<V> values() {
try (Jedis jedis = pool.getResource()) {
return jedis.hvals(redisKey)
.stream()
.map(v -> deserialize(v, valueClass))
.toList();
}
}

@Override
public Set<Entry<K, V>> entrySet() {
try (Jedis jedis = pool.getResource()) {
return jedis.hgetAll(redisKey).entrySet()
.stream()
.map(e -> new AbstractMap.SimpleEntry<>(
deserialize(e.getKey(), keyClass),
deserialize(e.getValue(), valueClass)))
.collect(Collectors.toSet());
}
}
}

31 changes: 31 additions & 0 deletions src/main/java/org/redis/model/Student.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.redis.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Objects;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private List<String> skills;

@Override
public boolean equals(Object object) {
if (object == null || getClass() != object.getClass()) return false;
Student student = (Student) object;
return age == student.age
&& Objects.equals(name, student.name)
&& Objects.equals(skills, student.skills);
}

@Override
public int hashCode() {
return Objects.hash(name, age, skills);
}
}
Loading