DATAREDIS-553 - Support caching null values via RedisCache using callable.
Store `NullValue` placeholders when null value caching is allowed. Remove cache values when null value caching is disabled and the value loader returns a `null` value. Original pull request: #221.
This commit is contained in:
@@ -51,6 +51,7 @@ import org.springframework.data.redis.core.AbstractOperationsTestParams;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import edu.umd.cs.mtc.MultithreadedTestCase;
|
||||
|
||||
@@ -381,6 +382,71 @@ public class RedisCacheTest extends AbstractNativeCacheTest<RedisTemplate> {
|
||||
assertThat(cache.get(key, String.class), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-553
|
||||
*/
|
||||
@Test
|
||||
public void testCacheGetSynchronizedNullAllowingNull() {
|
||||
|
||||
assumeThat(getAllowCacheNullValues(), is(true));
|
||||
assumeThat(cache, instanceOf(RedisCache.class));
|
||||
|
||||
Object key = getKey();
|
||||
Object value = cache.get(key, new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(value, is(nullValue()));
|
||||
assertThat(cache.get(key).get(), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-553
|
||||
*/
|
||||
@Test
|
||||
public void testCacheGetSynchronizedNullNotAllowingNull() {
|
||||
|
||||
assumeThat(getAllowCacheNullValues(), is(false));
|
||||
assumeThat(cache, instanceOf(RedisCache.class));
|
||||
assumeThat(template.getValueSerializer(), not(instanceOf(StringRedisSerializer.class)));
|
||||
|
||||
Object key = getKey();
|
||||
Object value = cache.get(key, new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(value, is(nullValue()));
|
||||
assertThat(cache.get(key), is(nullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-553
|
||||
*/
|
||||
@Test
|
||||
public void testCacheGetSynchronizedNullWithStoredNull() {
|
||||
|
||||
assumeThat(getAllowCacheNullValues(), is(true));
|
||||
assumeThat(cache, instanceOf(RedisCache.class));
|
||||
|
||||
Object key = getKey();
|
||||
cache.put(key, null);
|
||||
|
||||
Object cachedValue = cache.get(key, new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(cachedValue, is(nullValue()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class CacheGetWithValueLoaderIsThreadSafe extends MultithreadedTestCase {
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.NullValue;
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
@@ -138,7 +139,7 @@ public class RedisCacheUnitTests {
|
||||
* @see DATAREDIS-369
|
||||
*/
|
||||
@Test
|
||||
public void clearShouldCallLuaScritpToRemoveKeysWhenPrefixIsSet() {
|
||||
public void clearShouldCallLuaScriptToRemoveKeysWhenPrefixIsSet() {
|
||||
|
||||
cache = new RedisCache(CACHE_NAME, PREFIX_BYTES, templateSpy, EXPIRATION);
|
||||
cache.clear();
|
||||
@@ -214,7 +215,7 @@ public class RedisCacheUnitTests {
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getWithCallable() throws ClassNotFoundException, LinkageError {
|
||||
public void getWithCallable() throws ClassNotFoundException {
|
||||
|
||||
if (isPresent("org.springframework.cache.Cache$ValueRetrievalException", getDefaultClassLoader())) {
|
||||
exception.expect((Class<? extends Throwable>) forName("org.springframework.cache.Cache$ValueRetrievalException",
|
||||
@@ -235,6 +236,51 @@ public class RedisCacheUnitTests {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-553
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getWithCallableShouldStoreNullNotAllowingNull() throws ClassNotFoundException {
|
||||
|
||||
cache = new RedisCache(CACHE_NAME, NO_PREFIX_BYTES, templateSpy, 0L, false);
|
||||
|
||||
cache.get(KEY, new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
verify(connectionMock, times(1)).get(eq(KEY_BYTES));
|
||||
verify(connectionMock, times(1)).multi();
|
||||
verify(connectionMock, times(1)).del(eq(KEY_BYTES));
|
||||
verify(connectionMock, times(1)).exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-553
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getWithCallableShouldStoreNullAllowingNull() throws ClassNotFoundException {
|
||||
|
||||
cache = new RedisCache(CACHE_NAME, NO_PREFIX_BYTES, templateSpy, 0L, true);
|
||||
|
||||
cache.get(KEY, new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
verify(valueSerializerMock).serialize(isA(NullValue.class));
|
||||
verify(connectionMock, times(1)).get(eq(KEY_BYTES));
|
||||
verify(connectionMock, times(1)).multi();
|
||||
verify(connectionMock, times(1)).set(eq(KEY_BYTES), eq(VALUE_BYTES));
|
||||
verify(connectionMock, times(1)).exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREDIS-443
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user