From c40f153642f39d1bd2f67990ac2c8415f2e593df Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Jun 2026 10:00:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(cache):=20exists=20=E6=8E=92=E9=99=A4?= =?UTF-8?q?=E7=A9=BA=E5=80=BC=E6=A0=87=E8=AE=B0,=E4=B8=8E=20get=20?= =?UTF-8?q?=E5=8F=A3=E5=BE=84=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit exists 原用 hasKey,会把持有空值标记的 key 判为"存在"(true),而 get 对同一 key 返回 null,二者口径打架。改为读值判断:空标记/缺失均返回 false,真实值返回 true。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../template/RedisCacheEnhanceTemplate.java | 4 +++- .../RedisCacheEnhanceTemplateTest.java | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplate.java b/src/main/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplate.java index 2e6347d..b6f42fa 100644 --- a/src/main/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplate.java +++ b/src/main/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplate.java @@ -90,7 +90,9 @@ public class RedisCacheEnhanceTemplate { } public boolean exists(String key) { - return Boolean.TRUE.equals(redis.hasKey(keyBuilder.build(key))); + // 与 get 口径一致:空值标记不算"存在"(物理 hasKey 会把空标记算存在,与 get→null 打架) + String raw = redis.opsForValue().get(keyBuilder.build(key)); + return raw != null && !codec.isNullSentinel(raw); } // ---- 续期 ---- diff --git a/src/test/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplateTest.java b/src/test/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplateTest.java index 311f59d..6f4ab8c 100644 --- a/src/test/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplateTest.java +++ b/src/test/java/com/njcn/middle/cache/template/RedisCacheEnhanceTemplateTest.java @@ -23,6 +23,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; @@ -318,4 +319,24 @@ class RedisCacheEnhanceTemplateTest { void constructorAcceptsDefaults() { new RedisCacheEnhanceTemplate(redis, keyBuilder, codec, new RedisCacheProperties()); } + + // ---- #7: exists 排除空值标记(与 get 口径一致) ---- + + @Test + void existsTrueForRealValue() { + when(valueOps.get("cache:user:1")).thenReturn(JSON.toJSONString(new User(1L, "a"))); + assertTrue(template.exists("user:1")); + } + + @Test + void existsFalseForNullSentinel() { + when(valueOps.get("cache:user:1")).thenReturn(CacheConstant.NULL_SENTINEL); + assertFalse(template.exists("user:1")); + } + + @Test + void existsFalseForMissing() { + when(valueOps.get("cache:user:1")).thenReturn(null); + assertFalse(template.exists("user:1")); + } }