Skip to content

Commit 3a930ce

Browse files
christophstroblmp911de
authored andcommitted
Remove connection wrapping in StringRedisTemplate.
Wrapping is not required due to applying proper serializers. Also, the applied StringRedisConnection type cannot be easily consumed from StringRedisTemplate. See: #3191 Original pull request: #3193
1 parent cdf473b commit 3a930ce

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)