diff --git a/Code/spring-demo/pom.xml b/Code/spring-demo/pom.xml index a90248df3b609034f0765b79e00b28aa10e2f6c6..347aa548d3681e4d6b8403b192f26b4b5051a273 100644 --- a/Code/spring-demo/pom.xml +++ b/Code/spring-demo/pom.xml @@ -51,6 +51,11 @@ 1.8.0 + + org.springframework.boot + spring-boot-starter-data-redis + + mysql mysql-connector-java diff --git a/Code/spring-demo/src/main/java/com/wyz/springdemo/config/RedisConfig.java b/Code/spring-demo/src/main/java/com/wyz/springdemo/config/RedisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..e529312cd2d9bfdfcf904079df923b2e8780e810 --- /dev/null +++ b/Code/spring-demo/src/main/java/com/wyz/springdemo/config/RedisConfig.java @@ -0,0 +1,29 @@ +package com.wyz.springdemo.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@EnableCaching +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + redisTemplate.setValueSerializer(jsonRedisSerializer); + redisTemplate.setHashValueSerializer(jsonRedisSerializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} diff --git a/Code/spring-demo/src/main/java/com/wyz/springdemo/util/RedisUtil.java b/Code/spring-demo/src/main/java/com/wyz/springdemo/util/RedisUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..f3c4583a8ef8d546bd416bceab7e847f61cdd83b --- /dev/null +++ b/Code/spring-demo/src/main/java/com/wyz/springdemo/util/RedisUtil.java @@ -0,0 +1,109 @@ +package com.wyz.springdemo.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class RedisUtil { + + private final RedisTemplate redisTemplate; + + public RedisUtil(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + public boolean expire(String key, long timeout, TimeUnit unit) { + Boolean result = redisTemplate.expire(key, timeout, unit); + return Boolean.TRUE.equals(result); + } + + public long getExpire(String key) { + Long expire = redisTemplate.getExpire(key); + return expire == null ? 0L : expire; + } + + public boolean hasKey(String key) { + Boolean result = redisTemplate.hasKey(key); + return Boolean.TRUE.equals(result); + } + + public boolean delete(String key) { + Boolean result = redisTemplate.delete(key); + return Boolean.TRUE.equals(result); + } + + public long delete(Collection keys) { + Long deleted = redisTemplate.delete(keys); + return deleted == null ? 0L : deleted; + } + + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + public T get(String key, Class clazz) { + Object value = get(key); + if (value == null) { + return null; + } + return clazz.cast(value); + } + + public boolean set(String key, Object value) { + redisTemplate.opsForValue().set(key, value); + return true; + } + + public boolean set(String key, Object value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + return true; + } + + public long increment(String key, long delta) { + Long result = redisTemplate.opsForValue().increment(key, delta); + return result == null ? 0L : result; + } + + public long decrement(String key, long delta) { + Long result = redisTemplate.opsForValue().increment(key, -delta); + return result == null ? 0L : result; + } + + public Object hGet(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + public boolean hSet(String key, String item, Object value) { + redisTemplate.opsForHash().put(key, item, value); + return true; + } + + public boolean hSet(String key, String item, Object value, long timeout, TimeUnit unit) { + redisTemplate.opsForHash().put(key, item, value); + return expire(key, timeout, unit); + } + + public long hDelete(String key, Object... items) { + Long result = redisTemplate.opsForHash().delete(key, items); + return result == null ? 0L : result; + } + + public Set sMembers(String key) { + Set members = redisTemplate.opsForSet().members(key); + return members == null ? Collections.emptySet() : members; + } + + public long sAdd(String key, Object... values) { + Long result = redisTemplate.opsForSet().add(key, values); + return result == null ? 0L : result; + } + + public boolean sIsMember(String key, Object value) { + Boolean result = redisTemplate.opsForSet().isMember(key, value); + return Boolean.TRUE.equals(result); + } +} diff --git a/Code/spring-demo/src/main/resources/application.yml b/Code/spring-demo/src/main/resources/application.yml index 864cf74f2e2ab3088bf27c108a01172091f902e0..52f0234a12c77a27c9d5f1de4f92503f0ee9d5f2 100644 --- a/Code/spring-demo/src/main/resources/application.yml +++ b/Code/spring-demo/src/main/resources/application.yml @@ -9,6 +9,18 @@ spring: username: ${DB_USERNAME:root} password: ${DB_PASSWORD:123456} driver-class-name: com.mysql.cj.jdbc.Driver + redis: + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD:123456} + database: ${REDIS_DATABASE:0} + timeout: 5000ms + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 + max-wait: 1000ms mybatis: mapper-locations: classpath:/mapper/*.xml diff --git a/Code/spring-demo/src/test/java/com/wyz/springdemo/RedisUtilTest.java b/Code/spring-demo/src/test/java/com/wyz/springdemo/RedisUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..303bb2a2ae9fb164d442e36d53b865bd9b0d80cf --- /dev/null +++ b/Code/spring-demo/src/test/java/com/wyz/springdemo/RedisUtilTest.java @@ -0,0 +1,174 @@ +package com.wyz.springdemo; + +import com.wyz.springdemo.util.RedisUtil; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +@DisplayName("Redis 工具类测试") +class RedisUtilTest { + + @Autowired + private RedisUtil redisUtil; + + private static final String TEST_KEY = "test:key"; + private static final String TEST_VALUE = "hello-redis"; + + @AfterEach + void tearDown() { + redisUtil.delete(TEST_KEY); + redisUtil.delete("test:hash"); + redisUtil.delete("test:set"); + redisUtil.delete("test:counter"); + redisUtil.delete("test:expire"); + } + + @Test + @DisplayName("测试 String 类型的 set 和 get") + void testSetAndGet() { + boolean setResult = redisUtil.set(TEST_KEY, TEST_VALUE); + assertTrue(setResult); + + Object value = redisUtil.get(TEST_KEY); + assertEquals(TEST_VALUE, value); + } + + @Test + @DisplayName("测试带过期时间的 set") + void testSetWithExpire() throws InterruptedException { + redisUtil.set("test:expire", "temp-value", 2, TimeUnit.SECONDS); + + assertTrue(redisUtil.hasKey("test:expire")); + + Thread.sleep(2500); + + assertFalse(redisUtil.hasKey("test:expire")); + } + + @Test + @DisplayName("测试设置过期时间") + void testExpire() { + redisUtil.set(TEST_KEY, TEST_VALUE); + boolean expireResult = redisUtil.expire(TEST_KEY, 60, TimeUnit.SECONDS); + assertTrue(expireResult); + + long ttl = redisUtil.getExpire(TEST_KEY); + assertTrue(ttl > 0 && ttl <= 60); + } + + @Test + @DisplayName("测试判断 key 是否存在") + void testHasKey() { + assertFalse(redisUtil.hasKey(TEST_KEY)); + + redisUtil.set(TEST_KEY, TEST_VALUE); + assertTrue(redisUtil.hasKey(TEST_KEY)); + } + + @Test + @DisplayName("测试删除单个 key") + void testDeleteSingleKey() { + redisUtil.set(TEST_KEY, TEST_VALUE); + assertTrue(redisUtil.hasKey(TEST_KEY)); + + boolean deleteResult = redisUtil.delete(TEST_KEY); + assertTrue(deleteResult); + assertFalse(redisUtil.hasKey(TEST_KEY)); + } + + @Test + @DisplayName("测试批量删除 key") + void testDeleteMultipleKeys() { + redisUtil.set("test:key1", "value1"); + redisUtil.set("test:key2", "value2"); + redisUtil.set("test:key3", "value3"); + + long deleted = redisUtil.delete(Arrays.asList("test:key1", "test:key2", "test:key3")); + assertEquals(3, deleted); + } + + @Test + @DisplayName("测试自增与自减") + void testIncrementAndDecrement() { + redisUtil.set("test:counter", 10); + + long incremented = redisUtil.increment("test:counter", 5); + assertEquals(15, incremented); + + long decremented = redisUtil.decrement("test:counter", 3); + assertEquals(12, decremented); + } + + @Test + @DisplayName("测试 Hash 的 hSet 和 hGet") + void testHashSetAndGet() { + redisUtil.hSet("test:hash", "name", "张三"); + redisUtil.hSet("test:hash", "age", 25); + + assertEquals("张三", redisUtil.hGet("test:hash", "name")); + assertEquals(25, redisUtil.hGet("test:hash", "age")); + } + + @Test + @DisplayName("测试 Hash 删除字段") + void testHashDelete() { + redisUtil.hSet("test:hash", "field1", "value1"); + redisUtil.hSet("test:hash", "field2", "value2"); + + long deleted = redisUtil.hDelete("test:hash", "field1", "field2"); + assertEquals(2, deleted); + + assertNull(redisUtil.hGet("test:hash", "field1")); + assertNull(redisUtil.hGet("test:hash", "field2")); + } + + @Test + @DisplayName("测试 Set 的 sAdd 和 sMembers") + void testSetAddAndMembers() { + redisUtil.sAdd("test:set", "apple", "banana", "orange"); + + Set members = redisUtil.sMembers("test:set"); + assertNotNull(members); + assertEquals(3, members.size()); + assertTrue(members.contains("apple")); + assertTrue(members.contains("banana")); + assertTrue(members.contains("orange")); + } + + @Test + @DisplayName("测试 Set 判断成员是否存在") + void testSetIsMember() { + redisUtil.sAdd("test:set", "apple", "banana"); + + assertTrue(redisUtil.sIsMember("test:set", "apple")); + assertFalse(redisUtil.sIsMember("test:set", "orange")); + } + + @Test + @DisplayName("测试获取过期时间") + void testGetExpire() { + redisUtil.set(TEST_KEY, TEST_VALUE, 120, TimeUnit.SECONDS); + + long expire = redisUtil.getExpire(TEST_KEY); + assertTrue(expire > 0 && expire <= 120); + } + + @Test + @DisplayName("测试获取不存在的 key 返回 null") + void testGetNonExistentKey() { + Object value = redisUtil.get("non:existent:key"); + assertNull(value); + } +}