Skip to content

Commit 2f60083

Browse files
committed
Add integration tests for reused named parameters from bean properties
See gh-34768
1 parent b83e07f commit 2f60083

File tree

2 files changed

+187
-90
lines changed

2 files changed

+187
-90
lines changed

Diff for: spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientIntegrationTests.java

+63-28
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.junit.jupiter.api.AfterEach;
2222
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Nested;
2324
import org.junit.jupiter.api.Test;
2425

2526
import org.springframework.core.io.ClassRelativeResourceLoader;
@@ -145,50 +146,84 @@ void updateWithGeneratedKeysAndKeyColumnNamesUsingNamedParameters() {
145146
assertUser(expectedId, firstName, lastName);
146147
}
147148

148-
@Test // gh-34768
149-
void selectWithReusedNamedParameter() {
150-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "John").update();
151-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "Smith").update();
152-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("Smith", "Smith").update();
153-
assertNumUsers(4);
154149

155-
String sql = """
150+
@Nested // gh-34768
151+
class ReusedNamedParameterTests {
152+
153+
private static final String QUERY1 = """
156154
select * from users
157155
where
158156
first_name in ('Bogus', :name) or
159157
last_name in (:name, 'Bogus')
160158
order by last_name
161159
""";
162160

163-
List<User> users = this.jdbcClient.sql(sql)
164-
.param("name", "John")
165-
.query(User.class)
166-
.list();
167-
168-
assertThat(users).containsExactly(new User(2, "John", "John"), new User(3, "John", "Smith"));
169-
}
170-
171-
@Test // gh-34768
172-
void selectWithReusedNamedParameterList() {
173-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "John").update();
174-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "Smith").update();
175-
this.jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("Smith", "Smith").update();
176-
assertNumUsers(4);
177-
178-
String sql = """
161+
private static final String QUERY2 = """
179162
select * from users
180163
where
181164
first_name in (:names) or
182165
last_name in (:names)
183166
order by last_name
184167
""";
185168

186-
List<User> users = this.jdbcClient.sql(sql)
187-
.param("names", List.of("John", "Bogus"))
188-
.query(User.class)
189-
.list();
190169

191-
assertThat(users).containsExactly(new User(2, "John", "John"), new User(3, "John", "Smith"));
170+
@BeforeEach
171+
void insertTestUsers() {
172+
jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "John").update();
173+
jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("John", "Smith").update();
174+
jdbcClient.sql(INSERT_WITH_JDBC_PARAMS).params("Smith", "Smith").update();
175+
assertNumUsers(4);
176+
}
177+
178+
@Test
179+
void selectWithReusedNamedParameter() {
180+
List<User> users = jdbcClient.sql(QUERY1)
181+
.param("name", "John")
182+
.query(User.class)
183+
.list();
184+
185+
assertResults(users);
186+
}
187+
188+
@Test
189+
void selectWithReusedNamedParameterFromBeanProperties() {
190+
List<User> users = jdbcClient.sql(QUERY1)
191+
.paramSource(new Name("John"))
192+
.query(User.class)
193+
.list();
194+
195+
assertResults(users);
196+
}
197+
198+
@Test
199+
void selectWithReusedNamedParameterList() {
200+
List<User> users = jdbcClient.sql(QUERY2)
201+
.param("names", List.of("John", "Bogus"))
202+
.query(User.class)
203+
.list();
204+
205+
assertResults(users);
206+
}
207+
208+
@Test
209+
void selectWithReusedNamedParameterListFromBeanProperties() {
210+
List<User> users = jdbcClient.sql(QUERY2)
211+
.paramSource(new Names(List.of("John", "Bogus")))
212+
.query(User.class)
213+
.list();
214+
215+
assertResults(users);
216+
}
217+
218+
219+
private static void assertResults(List<User> users) {
220+
assertThat(users).containsExactly(new User(2, "John", "John"), new User(3, "John", "Smith"));
221+
}
222+
223+
record Name(String name) {}
224+
225+
record Names(List<String> names) {}
226+
192227
}
193228

194229

Diff for: spring-r2dbc/src/test/java/org/springframework/r2dbc/core/AbstractDatabaseClientIntegrationTests.java

+124-62
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.r2dbc.spi.Parameters;
2424
import io.r2dbc.spi.Result;
2525
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Nested;
2627
import org.junit.jupiter.api.Test;
2728
import reactor.core.publisher.Flux;
2829
import reactor.core.publisher.Mono;
@@ -38,6 +39,7 @@
3839
* @author Mark Paluch
3940
* @author Mingyuan Wu
4041
* @author Juergen Hoeller
42+
* @author Sam Brannen
4143
*/
4244
abstract class AbstractDatabaseClientIntegrationTests {
4345

@@ -121,7 +123,8 @@ void executeInsertWithMap() {
121123
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
122124

123125
databaseClient.sql("INSERT INTO legoset (id, name, manual) VALUES(:id, :name, :manual)")
124-
.bindValues(Map.of("id", 42055,
126+
.bindValues(Map.of(
127+
"id", 42055,
125128
"name", Parameters.in("SCHAUFELRADBAGGER"),
126129
"manual", Parameters.in(Integer.class)))
127130
.fetch().rowsUpdated()
@@ -199,8 +202,7 @@ void executeDeferred() {
199202
void shouldEmitGeneratedKey() {
200203
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
201204

202-
databaseClient.sql(
203-
"INSERT INTO legoset ( name, manual) VALUES(:name, :manual)")
205+
databaseClient.sql("INSERT INTO legoset ( name, manual) VALUES(:name, :manual)")
204206
.bind("name","SCHAUFELRADBAGGER")
205207
.bindNull("manual", Integer.class)
206208
.filter(statement -> statement.returnGeneratedValues("id"))
@@ -211,69 +213,129 @@ void shouldEmitGeneratedKey() {
211213
.verifyComplete();
212214
}
213215

214-
@Test // gh-34768
215-
void executeInsertWithReusedNamedParameter() {
216-
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
217-
218-
Lego lego = new Lego(1, 42, "Star Wars", 42);
219216

220-
databaseClient.sql(() -> "INSERT INTO legoset (id, version, name, manual) VALUES(:id, :number, :name, :number)")
221-
.bind("id", lego.id)
222-
.bind("name", lego.name)
223-
.bind("number", lego.version)
224-
.fetch().rowsUpdated()
225-
.as(StepVerifier::create)
226-
.expectNext(1L)
227-
.verifyComplete();
217+
@Nested
218+
class ReusedNamedParameterTests {
219+
220+
@Test // gh-34768
221+
void executeInsertWithReusedNamedParameter() {
222+
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
223+
224+
Lego lego = new Lego(1, 42, "Star Wars", 42);
225+
226+
// ":number" is reused.
227+
databaseClient.sql("INSERT INTO legoset (id, version, name, manual) VALUES(:id, :number, :name, :number)")
228+
.bind("id", lego.id)
229+
.bind("name", lego.name)
230+
.bind("number", lego.version)
231+
.fetch().rowsUpdated()
232+
.as(StepVerifier::create)
233+
.expectNext(1L)
234+
.verifyComplete();
235+
236+
databaseClient.sql("SELECT * FROM legoset")
237+
.mapProperties(Lego.class)
238+
.first()
239+
.as(StepVerifier::create)
240+
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
241+
.verifyComplete();
242+
}
243+
244+
@Test // gh-34768
245+
void executeSelectWithReusedNamedParameterList() {
246+
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
247+
248+
String insertSql = "INSERT INTO legoset (id, version, name, manual) VALUES(:id, :version, :name, :manual)";
249+
// ":numbers" is reused.
250+
String selectSql = "SELECT * FROM legoset WHERE version IN (:numbers) OR manual IN (:numbers)";
251+
Lego lego = new Lego(1, 42, "Star Wars", 99);
252+
253+
databaseClient.sql(insertSql)
254+
.bind("id", lego.id)
255+
.bind("version", lego.version)
256+
.bind("name", lego.name)
257+
.bind("manual", lego.manual)
258+
.fetch().rowsUpdated()
259+
.as(StepVerifier::create)
260+
.expectNext(1L)
261+
.verifyComplete();
262+
263+
databaseClient.sql(selectSql)
264+
// match version
265+
.bind("numbers", List.of(2, 3, lego.version, 4))
266+
.mapProperties(Lego.class)
267+
.first()
268+
.as(StepVerifier::create)
269+
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
270+
.verifyComplete();
271+
272+
databaseClient.sql(selectSql)
273+
// match manual
274+
.bind("numbers", List.of(2, 3, lego.manual, 4))
275+
.mapProperties(Lego.class)
276+
.first()
277+
.as(StepVerifier::create)
278+
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
279+
.verifyComplete();
280+
}
281+
282+
@Test // gh-34768
283+
void executeSelectWithReusedNamedParameterListFromBeanProperties() {
284+
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
285+
286+
String insertSql = "INSERT INTO legoset (id, version, name, manual) VALUES(:id, :version, :name, :manual)";
287+
// ":numbers" is reused.
288+
String selectSql = "SELECT * FROM legoset WHERE version IN (:numbers) OR manual IN (:numbers)";
289+
Lego lego = new Lego(1, 42, "Star Wars", 99);
290+
291+
databaseClient.sql(insertSql)
292+
.bind("id", lego.id)
293+
.bind("version", lego.version)
294+
.bind("name", lego.name)
295+
.bind("manual", lego.manual)
296+
.fetch().rowsUpdated()
297+
.as(StepVerifier::create)
298+
.expectNext(1L)
299+
.verifyComplete();
300+
301+
databaseClient.sql(selectSql)
302+
// match version
303+
.bindProperties(new LegoRequest(List.of(lego.version)))
304+
.mapProperties(Lego.class)
305+
.first()
306+
.as(StepVerifier::create)
307+
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
308+
.verifyComplete();
309+
310+
databaseClient.sql(selectSql)
311+
// match manual
312+
.bindProperties(new LegoRequest(List.of(lego.manual)))
313+
.mapProperties(Lego.class)
314+
.first()
315+
.as(StepVerifier::create)
316+
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
317+
.verifyComplete();
318+
}
319+
320+
321+
record Lego(int id, Integer version, String name, Integer manual) {
322+
}
323+
324+
static class LegoRequest {
325+
326+
private final List<Integer> numbers;
327+
328+
LegoRequest(List<Integer> numbers) {
329+
this.numbers = numbers;
330+
}
331+
332+
public List<Integer> getNumbers() {
333+
return numbers;
334+
}
335+
}
228336

229-
databaseClient.sql("SELECT * FROM legoset")
230-
.mapProperties(Lego.class)
231-
.first()
232-
.as(StepVerifier::create)
233-
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
234-
.verifyComplete();
235337
}
236338

237-
@Test // gh-34768
238-
void executeSelectWithReusedNamedParameterList() {
239-
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
240-
241-
String insertSql = "INSERT INTO legoset (id, version, name, manual) VALUES(:id, :version, :name, :manual)";
242-
String selectSql = "SELECT * FROM legoset WHERE version IN (:numbers) OR manual IN (:numbers)";
243-
Lego lego = new Lego(1, 42, "Star Wars", 99);
244-
245-
databaseClient.sql(insertSql)
246-
.bind("id", lego.id)
247-
.bind("version", lego.version)
248-
.bind("name", lego.name)
249-
.bind("manual", lego.manual)
250-
.fetch().rowsUpdated()
251-
.as(StepVerifier::create)
252-
.expectNext(1L)
253-
.verifyComplete();
254-
255-
databaseClient.sql(selectSql)
256-
// match version
257-
.bind("numbers", List.of(2, 3, lego.version, 4))
258-
.mapProperties(Lego.class)
259-
.first()
260-
.as(StepVerifier::create)
261-
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
262-
.verifyComplete();
263-
264-
databaseClient.sql(selectSql)
265-
// match manual
266-
.bind("numbers", List.of(2, 3, lego.manual, 4))
267-
.mapProperties(Lego.class)
268-
.first()
269-
.as(StepVerifier::create)
270-
.assertNext(actual -> assertThat(actual).isEqualTo(lego))
271-
.verifyComplete();
272-
}
273-
274-
275-
record Lego(int id, Integer version, String name, Integer manual) {
276-
}
277339

278340
record ParameterRecord(int id, String name, Integer manual) {
279341
}

0 commit comments

Comments
 (0)