diff --git a/src/main/java/org/springframework/data/redis/core/RedisCommand.java b/src/main/java/org/springframework/data/redis/core/RedisCommand.java index 90b794c22a..79b40842ed 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisCommand.java +++ b/src/main/java/org/springframework/data/redis/core/RedisCommand.java @@ -35,6 +35,7 @@ * @author Oscar Cai * @author Sébastien Volle * @author John Blum + * @author LeeHyungGeol * @since 1.3 * @link Redis @@ -292,12 +293,16 @@ public enum RedisCommand { ZINTERSTORE("rw", 3), // ZRANGE("r", 3), // ZRANGEBYSCORE("r", 3), // + ZRANGEWITHSCORES("r", 3), // + ZRANGEBYSCOREWITHSCORES("r", 2), // ZRANK("r", 2, 2), // ZREM("rw", 2), // ZREMRANGEBYRANK("rw", 3, 3), // ZREMRANGEBYSCORE("rw", 3, 3), // ZREVRANGE("r", 3), // ZREVRANGEBYSCORE("r", 3), // + ZREVRANGEWITHSCORES("r", 3), // + ZREVRANGEBYSCOREWITHSCORES("r", 2), // ZREVRANK("r", 2, 2), // ZSCORE("r", 2, 2), // ZUNIONSTORE("rw", 3), // diff --git a/src/test/java/org/springframework/data/redis/core/TransactionalStringRedisTemplateTests.java b/src/test/java/org/springframework/data/redis/core/TransactionalStringRedisTemplateTests.java index 259a40ef2b..c91ddd1e14 100644 --- a/src/test/java/org/springframework/data/redis/core/TransactionalStringRedisTemplateTests.java +++ b/src/test/java/org/springframework/data/redis/core/TransactionalStringRedisTemplateTests.java @@ -22,6 +22,7 @@ import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.stream.Stream; import javax.sql.DataSource; @@ -42,6 +43,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.ZSetOperations.TypedTuple; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; @@ -49,6 +51,7 @@ * Transactional integration tests for {@link StringRedisTemplate}. * * @author Christoph Strobl + * @author LeeHyungGeol */ @ParameterizedClass @MethodSource("argumentsStream") @@ -116,6 +119,96 @@ void visibilityDuringManagedTransaction() throws SQLException { .containsEntry("isMember(inside)", false); } + @SuppressWarnings("unchecked") + @Test // GH-3187 + void allRangeWithScoresMethodsShouldExecuteImmediatelyInTransaction() throws SQLException { + + DataSource ds = mock(DataSource.class); + when(ds.getConnection()).thenReturn(mock(Connection.class)); + + DataSourceTransactionManager txMgr = new DataSourceTransactionManager(ds); + TransactionTemplate txTemplate = new TransactionTemplate(txMgr); + txTemplate.afterPropertiesSet(); + + // Add data outside transaction + stringTemplate.opsForZSet().add("testzset", "outside1", 1.0); + stringTemplate.opsForZSet().add("testzset", "outside2", 2.0); + + Map result = txTemplate.execute(x -> { + Map ops = new LinkedHashMap<>(); + + // Query data added outside transaction (should execute immediately) + ops.put("rangeWithScores_before", + stringTemplate.opsForZSet().rangeWithScores("testzset", 0, -1)); + ops.put("reverseRangeWithScores_before", + stringTemplate.opsForZSet().reverseRangeWithScores("testzset", 0, -1)); + ops.put("rangeByScoreWithScores_before", + stringTemplate.opsForZSet().rangeByScoreWithScores("testzset", 1.0, 2.0)); + ops.put("reverseRangeByScoreWithScores_before", + stringTemplate.opsForZSet().reverseRangeByScoreWithScores("testzset", 1.0, 2.0)); + + // Add inside transaction (goes into multi/exec queue) + ops.put("add_result", stringTemplate.opsForZSet().add("testzset", "inside", 3.0)); + + // Changes made inside transaction should not be visible yet (read executes immediately) + ops.put("rangeWithScores_after", + stringTemplate.opsForZSet().rangeWithScores("testzset", 0, -1)); + ops.put("reverseRangeWithScores_after", + stringTemplate.opsForZSet().reverseRangeWithScores("testzset", 0, -1)); + ops.put("rangeByScoreWithScores_after", + stringTemplate.opsForZSet().rangeByScoreWithScores("testzset", 1.0, 3.0)); + ops.put("reverseRangeByScoreWithScores_after", + stringTemplate.opsForZSet().reverseRangeByScoreWithScores("testzset", 1.0, 3.0)); + + return ops; + }); + + // add result is null (no result until exec) + assertThat(result).containsEntry("add_result", null); + + // before: only data added outside transaction is visible + assertThat((Set>) result.get("rangeWithScores_before")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside1", "outside2"); + + assertThat((Set>) result.get("reverseRangeWithScores_before")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside2", "outside1"); + + assertThat((Set>) result.get("rangeByScoreWithScores_before")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside1", "outside2"); + + assertThat((Set>) result.get("reverseRangeByScoreWithScores_before")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside2", "outside1"); + + // after: changes made inside transaction are still not visible + assertThat((Set>) result.get("rangeWithScores_after")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside1", "outside2"); + + assertThat((Set>) result.get("reverseRangeWithScores_after")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside2", "outside1"); + + assertThat((Set>) result.get("rangeByScoreWithScores_after")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside1", "outside2"); + + assertThat((Set>) result.get("reverseRangeByScoreWithScores_after")) + .hasSize(2) + .extracting(TypedTuple::getValue) + .containsExactly("outside2", "outside1"); + } + static Stream argumentsStream() { LettuceConnectionFactory lcf = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration());