Skip to content

Commit d290625

Browse files
committed
Fall back to plain setObject call for non-supported SQL type
Closes gh-30556
1 parent 4b8adf2 commit d290625

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.sql.DatabaseMetaData;
2626
import java.sql.PreparedStatement;
2727
import java.sql.SQLException;
28+
import java.sql.SQLFeatureNotSupportedException;
2829
import java.sql.Types;
2930
import java.time.LocalDate;
3031
import java.time.LocalDateTime;
@@ -84,7 +85,7 @@ public abstract class StatementCreatorUtils {
8485

8586
private static final Log logger = LogFactory.getLog(StatementCreatorUtils.class);
8687

87-
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<>(32);
88+
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<>(64);
8889

8990
static {
9091
javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN);
@@ -106,8 +107,8 @@ public abstract class StatementCreatorUtils {
106107
javaTypeToSqlTypeMap.put(LocalDate.class, Types.DATE);
107108
javaTypeToSqlTypeMap.put(LocalTime.class, Types.TIME);
108109
javaTypeToSqlTypeMap.put(LocalDateTime.class, Types.TIMESTAMP);
109-
javaTypeToSqlTypeMap.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
110110
javaTypeToSqlTypeMap.put(OffsetTime.class, Types.TIME_WITH_TIMEZONE);
111+
javaTypeToSqlTypeMap.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
111112
javaTypeToSqlTypeMap.put(java.sql.Date.class, Types.DATE);
112113
javaTypeToSqlTypeMap.put(java.sql.Time.class, Types.TIME);
113114
javaTypeToSqlTypeMap.put(java.sql.Timestamp.class, Types.TIMESTAMP);
@@ -290,7 +291,19 @@ else if (typeName != null) {
290291
ps.setNull(paramIndex, sqlType, typeName);
291292
}
292293
else {
293-
ps.setNull(paramIndex, sqlType);
294+
// Fall back to generic setNull call.
295+
try {
296+
// Try generic setNull call with SQL type specified.
297+
ps.setNull(paramIndex, sqlType);
298+
}
299+
catch (SQLFeatureNotSupportedException ex) {
300+
if (sqlType == Types.NULL) {
301+
throw ex;
302+
}
303+
// Fall back to generic setNull call without SQL type specified
304+
// (e.g. for MySQL TIME_WITH_TIMEZONE / TIMESTAMP_WITH_TIMEZONE).
305+
ps.setNull(paramIndex, Types.NULL);
306+
}
294307
}
295308
}
296309

@@ -415,8 +428,16 @@ else if (inValue instanceof Calendar cal) {
415428
}
416429
}
417430
else {
418-
// Fall back to generic setObject call with SQL type specified.
419-
ps.setObject(paramIndex, inValue, sqlType);
431+
// Fall back to generic setObject call.
432+
try {
433+
// Try generic setObject call with SQL type specified.
434+
ps.setObject(paramIndex, inValue, sqlType);
435+
}
436+
catch (SQLFeatureNotSupportedException ex) {
437+
// Fall back to generic setObject call without SQL type specified
438+
// (e.g. for MySQL TIME_WITH_TIMEZONE / TIMESTAMP_WITH_TIMEZONE).
439+
ps.setObject(paramIndex, inValue);
440+
}
420441
}
421442
}
422443

spring-jdbc/src/test/java/org/springframework/jdbc/core/StatementCreatorUtilsTests.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import java.sql.ParameterMetaData;
2222
import java.sql.PreparedStatement;
2323
import java.sql.SQLException;
24+
import java.sql.SQLFeatureNotSupportedException;
2425
import java.sql.Types;
2526
import java.time.LocalDate;
2627
import java.time.LocalDateTime;
2728
import java.time.LocalTime;
29+
import java.time.OffsetDateTime;
2830
import java.time.ZoneOffset;
2931
import java.util.GregorianCalendar;
3032
import java.util.stream.Stream;
@@ -36,6 +38,7 @@
3638

3739
import static org.assertj.core.api.Assertions.assertThat;
3840
import static org.junit.jupiter.api.Named.named;
41+
import static org.mockito.BDDMockito.doThrow;
3942
import static org.mockito.BDDMockito.given;
4043
import static org.mockito.Mockito.mock;
4144
import static org.mockito.Mockito.never;
@@ -213,7 +216,6 @@ public void testSetParameterValueWithCalendarAndUnknownType() throws SQLExceptio
213216
verify(preparedStatement).setTimestamp(1, new java.sql.Timestamp(cal.getTime().getTime()), cal);
214217
}
215218

216-
217219
@ParameterizedTest
218220
@MethodSource("javaTimeTypes")
219221
public void testSetParameterValueWithJavaTimeTypes(Object o, int sqlType) throws SQLException {
@@ -242,6 +244,23 @@ static Stream<Arguments> javaTimeTypes() {
242244
);
243245
}
244246

247+
@Test // gh-30556
248+
public void testSetParameterValueWithOffsetDateTimeAndNotSupported() throws SQLException {
249+
OffsetDateTime time = OffsetDateTime.now();
250+
doThrow(new SQLFeatureNotSupportedException()).when(preparedStatement).setObject(1, time, Types.TIMESTAMP_WITH_TIMEZONE);
251+
StatementCreatorUtils.setParameterValue(preparedStatement, 1, Types.TIMESTAMP_WITH_TIMEZONE, null, time);
252+
verify(preparedStatement).setObject(1, time, Types.TIMESTAMP_WITH_TIMEZONE);
253+
verify(preparedStatement).setObject(1, time);
254+
}
255+
256+
@Test // gh-30556
257+
public void testSetParameterValueWithNullAndNotSupported() throws SQLException {
258+
doThrow(new SQLFeatureNotSupportedException()).when(preparedStatement).setNull(1, Types.TIMESTAMP_WITH_TIMEZONE);
259+
StatementCreatorUtils.setParameterValue(preparedStatement, 1, Types.TIMESTAMP_WITH_TIMEZONE, null, null);
260+
verify(preparedStatement).setNull(1, Types.TIMESTAMP_WITH_TIMEZONE);
261+
verify(preparedStatement).setNull(1, Types.NULL);
262+
}
263+
245264
@Test // SPR-8571
246265
public void testSetParameterValueWithStringAndVendorSpecificType() throws SQLException {
247266
Connection con = mock();

0 commit comments

Comments
 (0)