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 adcc88e65..c7af35f34 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -101,18 +101,17 @@ public class RedisCache implements Cache { public RedisCacheElement get(final RedisCacheKey cacheKey) { notNull(cacheKey, "CacheKey must not be null!"); - return (RedisCacheElement) redisOperations.execute(new AbstractRedisCacheCallback( - new RedisCacheElement(cacheKey, null), cacheMetadata) { + + byte[] bytes = (byte[]) redisOperations.execute(new AbstractRedisCacheCallback(new BinaryRedisCacheElement( + new RedisCacheElement(cacheKey, null), cacheValueAccessor), cacheMetadata) { @Override - public RedisCacheElement doInRedis(RedisCacheElement element, RedisConnection connection) - throws DataAccessException { - - byte[] bs = connection.get(element.getKeyBytes()); - Object value = redisOperations.getValueSerializer() != null ? redisOperations.getValueSerializer().deserialize(bs) : bs; - return (bs == null ? null : new RedisCacheElement(element.getKey(), value)); + public byte[] doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException { + return connection.get(element.getKeyBytes()); } }); + + return (bytes == null ? null : new RedisCacheElement(cacheKey, cacheValueAccessor.deserializeIfNecessary(bytes))); } /* @@ -137,7 +136,9 @@ public class RedisCache implements Cache { public void put(RedisCacheElement element) { notNull(element, "Element must not be null!"); - redisOperations.execute(new RedisCachePutCallback(element, cacheValueAccessor, cacheMetadata)); + + redisOperations.execute(new RedisCachePutCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), + cacheMetadata)); } /* @@ -147,7 +148,8 @@ public class RedisCache implements Cache { public ValueWrapper putIfAbsent(Object key, final Object value) { return putIfAbsent(new RedisCacheElement(new RedisCacheKey(key).usePrefix(cacheMetadata.getKeyPrefix()) - .withKeySerializer(redisOperations.getKeySerializer()), value).expireAfter(cacheMetadata.getDefaultExpiration())); + .withKeySerializer(redisOperations.getKeySerializer()), value) + .expireAfter(cacheMetadata.getDefaultExpiration())); } /** @@ -161,7 +163,12 @@ public class RedisCache implements Cache { public ValueWrapper putIfAbsent(RedisCacheElement element) { notNull(element, "Element must not be null!"); - return toWrapper(redisOperations.execute(new RedisCachePutIfAbsentCallback(element, cacheValueAccessor, cacheMetadata))); + + new RedisCachePutIfAbsentCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), cacheMetadata); + + return toWrapper(cacheValueAccessor.deserializeIfNecessary((byte[]) redisOperations + .execute(new RedisCachePutIfAbsentCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), + cacheMetadata)))); } /* @@ -180,7 +187,8 @@ public class RedisCache implements Cache { public void evict(final RedisCacheElement element) { notNull(element, "Element must not be null!"); - redisOperations.execute(new RedisCacheEvictCallback(element, cacheMetadata)); + redisOperations.execute(new RedisCacheEvictCallback(new BinaryRedisCacheElement(element, cacheValueAccessor), + cacheMetadata)); } /* @@ -323,6 +331,10 @@ public class RedisCache implements Cache { byte[] convertToBytesIfNecessary(Object value) { + if (value == null) { + return new byte[0]; + } + if (valueSerializer == null && value instanceof byte[]) { return (byte[]) value; } @@ -340,6 +352,52 @@ public class RedisCache implements Cache { } } + /** + * @author Christoph Strobl + * @since 1.6 + */ + static class BinaryRedisCacheElement extends RedisCacheElement { + + private byte[] keyBytes; + private byte[] valueBytes; + private RedisCacheElement element; + + public BinaryRedisCacheElement(RedisCacheElement element, CacheValueAccessor accessor) { + + super(element.getKey(), element.get()); + this.element = element; + this.keyBytes = element.getKeyBytes(); + this.valueBytes = accessor.convertToBytesIfNecessary(element.get()); + } + + @Override + public byte[] getKeyBytes() { + return keyBytes; + } + + public long getTimeToLive() { + return element.getTimeToLive(); + } + + public boolean hasKeyPrefix() { + return element.hasKeyPrefix(); + } + + public boolean isEternal() { + return element.isEternal(); + } + + public RedisCacheElement expireAfter(long seconds) { + return element.expireAfter(seconds); + } + + @Override + public byte[] get() { + return valueBytes; + } + + } + /** * @author Christoph Strobl * @since 1.5 @@ -348,10 +406,10 @@ public class RedisCache implements Cache { static abstract class AbstractRedisCacheCallback implements RedisCallback { private long WAIT_FOR_LOCK_TIMEOUT = 300; - private final RedisCacheElement element; + private final BinaryRedisCacheElement element; private final RedisCacheMetadata cacheMetadata; - public AbstractRedisCacheCallback(RedisCacheElement element, RedisCacheMetadata metadata) { + public AbstractRedisCacheCallback(BinaryRedisCacheElement element, RedisCacheMetadata metadata) { this.element = element; this.cacheMetadata = metadata; } @@ -366,7 +424,7 @@ public class RedisCache implements Cache { return doInRedis(element, connection); } - public abstract T doInRedis(RedisCacheElement element, RedisConnection connection) throws DataAccessException; + public abstract T doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException; protected void processKeyExpiration(RedisCacheElement element, RedisConnection connection) { if (!element.isEternal()) { @@ -526,7 +584,7 @@ public class RedisCache implements Cache { */ static class RedisCacheEvictCallback extends AbstractRedisCacheCallback { - public RedisCacheEvictCallback(RedisCacheElement element, RedisCacheMetadata metadata) { + public RedisCacheEvictCallback(BinaryRedisCacheElement element, RedisCacheMetadata metadata) { super(element, metadata); } @@ -535,7 +593,7 @@ public class RedisCache implements Cache { * @see org.springframework.data.redis.cache.RedisCache.AbstractRedisCacheCallback#doInRedis(org.springframework.data.redis.cache.RedisCacheElement, org.springframework.data.redis.connection.RedisConnection) */ @Override - public Void doInRedis(RedisCacheElement element, RedisConnection connection) throws DataAccessException { + public Void doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException { connection.del(element.getKeyBytes()); cleanKnownKeys(element, connection); @@ -549,13 +607,9 @@ public class RedisCache implements Cache { */ static class RedisCachePutCallback extends AbstractRedisCacheCallback { - private final CacheValueAccessor valueAccessor; - - public RedisCachePutCallback(RedisCacheElement element, CacheValueAccessor valueAccessor, - RedisCacheMetadata metadata) { + public RedisCachePutCallback(BinaryRedisCacheElement element, RedisCacheMetadata metadata) { super(element, metadata); - this.valueAccessor = valueAccessor; } /* @@ -563,11 +617,11 @@ public class RedisCache implements Cache { * @see org.springframework.data.redis.cache.RedisCache.AbstractRedisPutCallback#doInRedis(org.springframework.data.redis.cache.RedisCache.RedisCacheElement, org.springframework.data.redis.connection.RedisConnection) */ @Override - public Void doInRedis(RedisCacheElement element, RedisConnection connection) throws DataAccessException { + public Void doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException { connection.multi(); - connection.set(element.getKeyBytes(), valueAccessor.convertToBytesIfNecessary(element.get())); + connection.set(element.getKeyBytes(), element.get()); processKeyExpiration(element, connection); maintainKnownKeys(element, connection); @@ -581,15 +635,10 @@ public class RedisCache implements Cache { * @author Christoph Strobl * @since 1.5 */ - static class RedisCachePutIfAbsentCallback extends AbstractRedisCacheCallback { - - private final CacheValueAccessor valueAccessor; - - public RedisCachePutIfAbsentCallback(RedisCacheElement element, CacheValueAccessor valueAccessor, - RedisCacheMetadata metadata) { + static class RedisCachePutIfAbsentCallback extends AbstractRedisCacheCallback { + public RedisCachePutIfAbsentCallback(BinaryRedisCacheElement element, RedisCacheMetadata metadata) { super(element, metadata); - this.valueAccessor = valueAccessor; } /* @@ -597,10 +646,10 @@ public class RedisCache implements Cache { * @see org.springframework.data.redis.cache.RedisCache.AbstractRedisPutCallback#doInRedis(org.springframework.data.redis.cache.RedisCache.RedisCacheElement, org.springframework.data.redis.connection.RedisConnection) */ @Override - public Object doInRedis(RedisCacheElement element, RedisConnection connection) throws DataAccessException { + public byte[] doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException { waitForLock(connection); - Object resultValue = put(element, connection); + byte[] resultValue = put(element, connection); if (nullSafeEquals(element.get(), resultValue)) { processKeyExpiration(element, connection); @@ -610,11 +659,10 @@ public class RedisCache implements Cache { return resultValue; } - private Object put(RedisCacheElement element, RedisConnection connection) { + private byte[] put(BinaryRedisCacheElement element, RedisConnection connection) { - boolean valueWasSet = connection.setNX(element.getKeyBytes(), - valueAccessor.convertToBytesIfNecessary(element.get())); - return valueWasSet ? null : valueAccessor.deserializeIfNecessary(connection.get(element.getKeyBytes())); + boolean valueWasSet = connection.setNX(element.getKeyBytes(), element.get()); + return valueWasSet ? null : connection.get(element.getKeyBytes()); } }