diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index 8d2706836..95ca9c0d0 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -137,6 +137,38 @@ class RedisCache implements Cache { }, true); } + public ValueWrapper putIfAbsent(Object key, final Object value) { + + final byte[] keyBytes = computeKey(key); + final byte[] valueBytes = convertToBytesIfNecessary(template.getValueSerializer(), value); + + return toWrapper(template.execute(new RedisCallback() { + public Object doInRedis(RedisConnection connection) throws DataAccessException { + + waitForLock(connection); + + Object resultValue = value; + boolean valueWasSet = connection.setNX(keyBytes, valueBytes); + if (valueWasSet) { + connection.zAdd(setName, 0, keyBytes); + if (expiration > 0) { + connection.expire(keyBytes, expiration); + // update the expiration of the set of keys as well + connection.expire(setName, expiration); + } + } else { + resultValue = deserializeIfNecessary(template.getValueSerializer(), connection.get(keyBytes)); + } + + return resultValue; + } + }, true)); + } + + private ValueWrapper toWrapper(Object value) { + return (value != null ? new SimpleValueWrapper(value) : null); + } + public void evict(Object key) { final byte[] k = computeKey(key); @@ -227,4 +259,14 @@ class RedisCache implements Cache { return serializer.serialize(value); } + + private Object deserializeIfNecessary(RedisSerializer serializer, byte[] value) { + + if (serializer != null) { + return serializer.deserialize(value); + } + + return value; + } + } diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheTest.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheTest.java index 7400a6e73..7db19c1ec 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheTest.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheTest.java @@ -16,14 +16,17 @@ package org.springframework.data.redis.cache; -import static org.hamcrest.core.IsInstanceOf.*; -import static org.hamcrest.core.IsNull.*; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsNull.nullValue; +import static org.hamcrest.core.IsSame.sameInstance; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.*; +import static org.junit.Assume.assumeThat; import static org.springframework.data.redis.matcher.RedisTestMatchers.isEqual; import java.util.Collection; @@ -71,7 +74,7 @@ public class RedisCacheTest extends AbstractNativeCacheTest { } @SuppressWarnings("unchecked") - protected Cache createCache(RedisTemplate nativeCache) { + protected RedisCache createCache(RedisTemplate nativeCache) { return new RedisCache(CACHE_NAME, CACHE_NAME.concat(":").getBytes(), nativeCache, TimeUnit.MINUTES.toSeconds(10)); } @@ -246,4 +249,31 @@ public class RedisCacheTest extends AbstractNativeCacheTest { Object invalidKey = template.getKeySerializer() == null ? "spring-data-redis".getBytes() : "spring-data-redis"; assertThat(redisCache.get(invalidKey, value.getClass()), nullValue()); } + + /** + * @see DATAREDIS-344 + */ + @Test + public void putIfAbsentShouldSetValueOnlyIfNotPresent() { + + assumeThat(cache, instanceOf(RedisCache.class)); + + RedisCache redisCache = (RedisCache)cache; + + Object key = getKey(); + template.delete(key); + + Object value = getValue(); + ValueWrapper wrapper = redisCache.putIfAbsent(key, value); + + assertThat(wrapper.get(), sameInstance(value)); + + ValueWrapper wrapper2 = redisCache.putIfAbsent(key, value); + + if (!(value instanceof Number)) { + assertThat(wrapper2.get(), not(sameInstance(value))); + } + + assertThat(wrapper2.get(), equalTo(value)); + } }