diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisOperations.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisOperations.java index f9c4411c3..b0e03eba5 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisOperations.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisOperations.java @@ -64,6 +64,17 @@ public interface RedisOperations { */ T execute(SessionCallback session); + /** + * Executes the given action object on a pipelined connection, returning the results. Note that the callback cannot + * return a non-null value as it gets overwritten by the pipeline. + * + * @param list element return type + * @param action callback object to execute + * @return list of objects returned by the pipeline + */ + List executePipelined(RedisCallback action); + + Boolean hasKey(K key); void delete(K key); diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java index 5b4442dcd..b92251df4 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/RedisTemplate.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.keyvalue.redis.connection.DataType; import org.springframework.data.keyvalue.redis.connection.RedisConnection; import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory; @@ -151,12 +152,14 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } /** - * Executes the given action object within a connection, that can be pipelined or not and which can be exposed or not. + * Executes the given action object within a connection that can be exposed or not. Additionally, the connection + * can be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios). + * Use {@link #executePipelined(RedisCallback)} as an alternative. * * @param return type * @param action callback object to execute * @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code - * @param pipeline whether to pipeline or not the connection for the execution duration + * @param pipeline whether to pipeline or not the connection for the execution * @return object returned by the action */ public T execute(RedisCallback action, boolean exposeConnection, boolean pipeline) { @@ -189,6 +192,48 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation } } + + @Override + public T execute(SessionCallback session) { + RedisConnectionFactory factory = getConnectionFactory(); + // bind connection + RedisConnectionUtils.bindConnection(factory); + try { + return session.execute(this); + } finally { + RedisConnectionUtils.unbindConnection(factory); + } + } + + @Override + @SuppressWarnings("unchecked") + public List executePipelined(final RedisCallback action) { + return executePipelined(action, valueSerializer); + } + + /** + * Executes the given action object on a pipelined connection, returning the results using a dedicated serializer. + * Note that the callback cannot return a non-null value as it gets overwritten by the pipeline. + * + * @param action callback object to execute + * @param resultSerializer + * @return list of objects returned by the pipeline + */ + public List executePipelined(final RedisCallback action, final RedisSerializer resultSerializer) { + return execute(new RedisCallback>() { + public List doInRedis(RedisConnection connection) throws DataAccessException { + connection.openPipeline(); + Object result = action.doInRedis(connection); + if (result != null) { + throw new InvalidDataAccessApiUsageException( + "Callback cannot returned a non-null value as it gets overwritten by the pipeline"); + } + List pipeline = connection.closePipeline(); + return SerializationUtils.deserialize(pipeline, resultSerializer); + } + }); + } + protected RedisConnection createRedisConnectionProxy(RedisConnection pm) { Class[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), getClass().getClassLoader()); return (RedisConnection) Proxy.newProxyInstance(pm.getClass().getClassLoader(), ifcs, @@ -208,18 +253,6 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation return result; } - @Override - public T execute(SessionCallback session) { - RedisConnectionFactory factory = getConnectionFactory(); - // bind connection - RedisConnectionUtils.bindConnection(factory); - try { - return session.execute(this); - } finally { - RedisConnectionUtils.unbindConnection(factory); - } - } - /** * Returns whether to expose the native Redis connection to RedisCallback code, or rather a connection proxy (the default). * diff --git a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/SessionCallback.java b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/SessionCallback.java index d80e2ca3a..8af247965 100644 --- a/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/SessionCallback.java +++ b/spring-data-redis/src/main/java/org/springframework/data/keyvalue/redis/core/SessionCallback.java @@ -15,6 +15,8 @@ */ package org.springframework.data.keyvalue.redis.core; +import org.springframework.dao.DataAccessException; + /** * Callback executing all operations against a surrogate 'session' (basically against the same underlying Redis connection). * Allows 'transactions' to take place through the use of multi/discard/exec/watch/unwatch commands. @@ -29,5 +31,5 @@ public interface SessionCallback { * @param operations Redis operations * @return return value */ - T execute(RedisOperations operations); + T execute(RedisOperations operations) throws DataAccessException; } diff --git a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/core/SessionTest.java b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/core/SessionTest.java index eb9e6c559..f550facfb 100644 --- a/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/core/SessionTest.java +++ b/spring-data-redis/src/test/java/org/springframework/data/keyvalue/redis/core/SessionTest.java @@ -36,7 +36,7 @@ public class SessionTest { when(factory.getConnection()).thenReturn(conn); final StringRedisTemplate template = new StringRedisTemplate(factory); - template.execute(new SessionCallback() { + template.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) { checkConnection(template, conn); @@ -48,7 +48,7 @@ public class SessionTest { }); } - private void checkConnection(RedisTemplate template, final RedisConnection expectedConnection) { + private void checkConnection(RedisTemplate template, final RedisConnection expectedConnection) { template.execute(new RedisCallback() { @Override