Skip to content

Commit 565f701

Browse files
Remove connection wrapping in StringRedisTemplate
1 parent 6ac045d commit 565f701

File tree

3 files changed

+144
-7
lines changed

3 files changed

+144
-7
lines changed

src/main/java/org/springframework/data/redis/core/StringRedisTemplate.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,4 @@ public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
5555
setConnectionFactory(connectionFactory);
5656
afterPropertiesSet();
5757
}
58-
59-
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
60-
return new DefaultStringRedisConnection(connection);
61-
}
62-
6358
}

src/test/java/org/springframework/data/redis/core/RedisTemplateIntegrationTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.data.redis.Person;
3838
import org.springframework.data.redis.SettingsUtils;
3939
import org.springframework.data.redis.connection.DataType;
40+
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
4041
import org.springframework.data.redis.connection.ExpirationOptions;
4142
import org.springframework.data.redis.connection.RedisClusterConnection;
4243
import org.springframework.data.redis.connection.RedisConnection;
@@ -161,7 +162,7 @@ void testTemplateNotInitialized() throws Exception {
161162
void testStringTemplateExecutesWithStringConn() {
162163
assumeThat(redisTemplate instanceof StringRedisTemplate).isTrue();
163164
String value = redisTemplate.execute((RedisCallback<String>) connection -> {
164-
StringRedisConnection stringConn = (StringRedisConnection) connection;
165+
StringRedisConnection stringConn = new DefaultStringRedisConnection(connection);
165166
stringConn.set("test", "it");
166167
return stringConn.get("test");
167168
});
@@ -307,7 +308,7 @@ void testExecutePipelinedCustomSerializer() {
307308
assumeThat(redisTemplate instanceof StringRedisTemplate).isTrue();
308309

309310
List<Object> results = redisTemplate.executePipelined((RedisCallback) connection -> {
310-
StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
311+
StringRedisConnection stringRedisConn = new DefaultStringRedisConnection(connection);
311312
stringRedisConn.set("foo", "5");
312313
stringRedisConn.get("foo");
313314
stringRedisConn.rPush("foolist", "10");
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.core;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.sql.Connection;
21+
import java.sql.SQLException;
22+
import java.util.LinkedHashMap;
23+
import java.util.Map;
24+
import java.util.stream.Stream;
25+
26+
import javax.sql.DataSource;
27+
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.params.AfterParameterizedClassInvocation;
32+
import org.junit.jupiter.params.BeforeParameterizedClassInvocation;
33+
import org.junit.jupiter.params.ParameterizedClass;
34+
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
35+
import org.junit.jupiter.params.provider.Arguments;
36+
import org.junit.jupiter.params.provider.MethodSource;
37+
import org.mockito.Mockito;
38+
import org.springframework.context.Lifecycle;
39+
import org.springframework.data.redis.SettingsUtils;
40+
import org.springframework.data.redis.connection.RedisConnectionFactory;
41+
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
42+
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
43+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
44+
import org.springframework.transaction.support.TransactionTemplate;
45+
46+
/**
47+
* @author Christoph Strobl
48+
*/
49+
@ParameterizedClass
50+
@MethodSource("argumentsStream")
51+
public class TransactionalStringRedisTemplateTests {
52+
53+
RedisConnectionFactory redisConnectionFactory;
54+
StringRedisTemplate stringTemplate;
55+
56+
TransactionalStringRedisTemplateTests(RedisConnectionFactory redisConnectionFactory) {
57+
this.redisConnectionFactory = redisConnectionFactory;
58+
59+
if (redisConnectionFactory instanceof Lifecycle lifecycleBean) {
60+
lifecycleBean.start();
61+
}
62+
}
63+
64+
@BeforeEach
65+
void beforeEach() {
66+
67+
stringTemplate = new StringRedisTemplate(redisConnectionFactory);
68+
69+
// explicitly enable transaction support
70+
stringTemplate.setEnableTransactionSupport(true);
71+
stringTemplate.afterPropertiesSet();
72+
73+
stringTemplate.execute((RedisCallback) con -> {
74+
con.flushDb();
75+
return null;
76+
});
77+
}
78+
79+
@AfterEach
80+
void afterEach() {
81+
redisConnectionFactory.getConnection().flushAll();
82+
}
83+
84+
@Test // GH-3191
85+
void visibilityDuringManagedTransaction() throws SQLException {
86+
87+
stringTemplate.opsForSet().add("myset", "outside");
88+
89+
DataSource ds = Mockito.mock(DataSource.class);
90+
Mockito.when(ds.getConnection()).thenReturn(Mockito.mock(Connection.class));
91+
92+
DataSourceTransactionManager txMgr = new DataSourceTransactionManager(ds);
93+
94+
TransactionTemplate txTemplate = new TransactionTemplate(txMgr);
95+
txTemplate.afterPropertiesSet();
96+
Map<String, Object> result = txTemplate.execute(x -> {
97+
98+
Map<String, Object> operationAndOutput = new LinkedHashMap<>();
99+
// visible since set outside of tx
100+
operationAndOutput.put("isMember(outside)", stringTemplate.opsForSet().isMember("myset", "outside"));
101+
102+
// add happens inside multi/exec
103+
operationAndOutput.put("add", stringTemplate.opsForSet().add("myset", "inside"));
104+
105+
// changes not visible though inside of tx, but command is not part of multi/exec block
106+
operationAndOutput.put("isMember(inside)", stringTemplate.opsForSet().isMember("myset", "inside"));
107+
108+
return operationAndOutput;
109+
});
110+
111+
assertThat(result).containsEntry("isMember(outside)", true).containsEntry("add", null)
112+
.containsEntry("isMember(inside)", false);
113+
}
114+
115+
static Stream<Arguments> argumentsStream() {
116+
117+
LettuceConnectionFactory lcf = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration());
118+
lcf.afterPropertiesSet();
119+
120+
JedisConnectionFactory jcf = new JedisConnectionFactory(SettingsUtils.standaloneConfiguration());
121+
jcf.afterPropertiesSet();
122+
123+
return Stream.of(Arguments.of(lcf), Arguments.of(jcf));
124+
}
125+
126+
@AfterParameterizedClassInvocation
127+
static void afterInvocation(ArgumentsAccessor accessor) {
128+
Object o = accessor.get(0);
129+
if (o instanceof Lifecycle lifecycle) {
130+
lifecycle.stop();
131+
}
132+
}
133+
134+
@BeforeParameterizedClassInvocation
135+
static void beforeInvocation(ArgumentsAccessor accessor) {
136+
Object o = accessor.get(0);
137+
if (o instanceof Lifecycle lifecycle) {
138+
lifecycle.start();
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)