Skip to content

Commit df5489b

Browse files
committed
Introduce optionalValue() method on ResultQuerySpec
Closes gh-33560
1 parent 24a8f1b commit df5489b

File tree

2 files changed

+63
-13
lines changed

2 files changed

+63
-13
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/JdbcClient.java

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -343,12 +343,26 @@ interface ResultQuerySpec {
343343

344344
/**
345345
* Retrieve a single value result.
346-
* @return the single row represented as its single column value
346+
* <p>Note: As of 6.2, this will enforce non-null result values
347+
* as originally designed (just accidentally not enforced before).
348+
* (never {@code null})
349+
* @see #optionalValue()
347350
* @see DataAccessUtils#requiredSingleResult(Collection)
348351
*/
349352
default Object singleValue() {
350353
return DataAccessUtils.requiredSingleResult(singleColumn());
351354
}
355+
356+
/**
357+
* Retrieve a single value result, if available, as an {@link Optional} handle.
358+
* @return an Optional handle with the single column value from the single row
359+
* @since 6.2
360+
* @see #singleValue()
361+
* @see DataAccessUtils#optionalResult(Collection)
362+
*/
363+
default Optional<Object> optionalValue() {
364+
return DataAccessUtils.optionalResult(singleColumn());
365+
}
352366
}
353367

354368

@@ -384,25 +398,27 @@ default Set<T> set() {
384398
return new LinkedHashSet<>(list());
385399
}
386400

387-
/**
388-
* Retrieve a single result, if available, as an {@link Optional} handle.
389-
* @return an Optional handle with a single result object or none
390-
* @see #list()
391-
* @see DataAccessUtils#optionalResult(Collection)
392-
*/
393-
default Optional<T> optional() {
394-
return DataAccessUtils.optionalResult(list());
395-
}
396-
397401
/**
398402
* Retrieve a single result as a required object instance.
403+
* <p>Note: As of 6.2, this will enforce non-null result values
404+
* as originally designed (just accidentally not enforced before).
399405
* @return the single result object (never {@code null})
400-
* @see #list()
406+
* @see #optional()
401407
* @see DataAccessUtils#requiredSingleResult(Collection)
402408
*/
403409
default T single() {
404410
return DataAccessUtils.requiredSingleResult(list());
405411
}
412+
413+
/**
414+
* Retrieve a single result, if available, as an {@link Optional} handle.
415+
* @return an Optional handle with a single result object or none
416+
* @see #single()
417+
* @see DataAccessUtils#optionalResult(Collection)
418+
*/
419+
default Optional<T> optional() {
420+
return DataAccessUtils.optionalResult(list());
421+
}
406422
}
407423

408424
}

spring-jdbc/src/test/java/org/springframework/jdbc/core/simple/JdbcClientQueryTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,40 @@ void queryForIntegerWithIndexedParamAndSingleValue() throws Exception {
174174
verify(connection).close();
175175
}
176176

177+
@Test
178+
void queryForIntegerWithIndexedParamAndOptionalValue() throws Exception {
179+
given(resultSet.next()).willReturn(true, false);
180+
given(resultSet.getObject(1)).willReturn(22);
181+
182+
Optional<Object> value = client.sql("SELECT AGE FROM CUSTMR WHERE ID = ?")
183+
.param(1, 3)
184+
.query().optionalValue();
185+
186+
assertThat(value.isPresent()).isTrue();
187+
assertThat(value.get()).isEqualTo(22);
188+
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
189+
verify(preparedStatement).setObject(1, 3);
190+
verify(resultSet).close();
191+
verify(preparedStatement).close();
192+
verify(connection).close();
193+
}
194+
195+
@Test
196+
void queryForIntegerWithIndexedParamAndNonExistingValue() throws Exception {
197+
given(resultSet.next()).willReturn(false);
198+
199+
Optional<Object> value = client.sql("SELECT AGE FROM CUSTMR WHERE ID = ?")
200+
.param(1, 3)
201+
.query().optionalValue();
202+
203+
assertThat(value.isPresent()).isFalse();
204+
verify(connection).prepareStatement("SELECT AGE FROM CUSTMR WHERE ID = ?");
205+
verify(preparedStatement).setObject(1, 3);
206+
verify(resultSet).close();
207+
verify(preparedStatement).close();
208+
verify(connection).close();
209+
}
210+
177211
@Test
178212
void queryForIntegerWithIndexedParamAndRowMapper() throws Exception {
179213
given(resultSet.next()).willReturn(true, false);

0 commit comments

Comments
 (0)