Skip to content

Commit 3767938

Browse files
committed
JdbcUtils uses JDBC 4.1 getObject(int, Class) for unknown ResultSet value types
Comes with general JDBC 3.0+ baseline upgrade, removing defensive measures. Issue: SPR-11600
1 parent 1bbc55d commit 3767938

File tree

5 files changed

+48
-67
lines changed

5 files changed

+48
-67
lines changed

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

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -30,7 +30,6 @@
3030
import java.util.Set;
3131

3232
import org.springframework.dao.InvalidDataAccessApiUsageException;
33-
import org.springframework.dao.InvalidDataAccessResourceUsageException;
3433
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
3534
import org.springframework.util.Assert;
3635

@@ -228,18 +227,11 @@ public PreparedStatementCreatorImpl(String actualSql, List<?> parameters) {
228227
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
229228
PreparedStatement ps;
230229
if (generatedKeysColumnNames != null || returnGeneratedKeys) {
231-
try {
232-
if (generatedKeysColumnNames != null) {
233-
ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
234-
}
235-
else {
236-
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
237-
}
230+
if (generatedKeysColumnNames != null) {
231+
ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
238232
}
239-
catch (AbstractMethodError err) {
240-
throw new InvalidDataAccessResourceUsageException(
241-
"Your JDBC driver is not compliant with JDBC 3.0 - " +
242-
"it does not support retrieval of auto-generated keys", err);
233+
else {
234+
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
243235
}
244236
}
245237
else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,8 @@ public abstract class StatementCreatorUtils {
8787
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<Class<?>, Integer>(32);
8888

8989
static {
90-
/* JDBC 3.0 only - not compatible with e.g. MySQL at present
91-
javaTypeToSqlTypeMap.put(boolean.class, new Integer(Types.BOOLEAN));
92-
javaTypeToSqlTypeMap.put(Boolean.class, new Integer(Types.BOOLEAN));
93-
*/
90+
javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN);
91+
javaTypeToSqlTypeMap.put(Boolean.class, Types.BOOLEAN);
9492
javaTypeToSqlTypeMap.put(byte.class, Types.TINYINT);
9593
javaTypeToSqlTypeMap.put(Byte.class, Types.TINYINT);
9694
javaTypeToSqlTypeMap.put(short.class, Types.SMALLINT);

spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.jdbc.datasource;
1818

19+
import java.sql.SQLException;
1920
import java.sql.Savepoint;
2021

2122
import org.apache.commons.logging.Log;
@@ -41,9 +42,6 @@
4142
* will automatically delegate to this, as it autodetects transaction
4243
* objects that implement the SavepointManager interface.
4344
*
44-
* <p>Note that savepoints are only supported for drivers which
45-
* support JDBC 3.0 or higher.
46-
*
4745
* @author Juergen Hoeller
4846
* @since 1.1
4947
*/
@@ -109,15 +107,9 @@ public Object createSavepoint() throws TransactionException {
109107
throw new NestedTransactionNotSupportedException(
110108
"Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
111109
}
112-
}
113-
catch (Throwable ex) {
114-
throw new NestedTransactionNotSupportedException(
115-
"Cannot create a nested transaction because your JDBC driver is not a JDBC 3.0 driver", ex);
116-
}
117-
try {
118110
return conHolder.createSavepoint();
119111
}
120-
catch (Throwable ex) {
112+
catch (SQLException ex) {
121113
throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
122114
}
123115
}

spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -26,16 +26,17 @@
2626
import java.sql.ResultSet;
2727
import java.sql.ResultSetMetaData;
2828
import java.sql.SQLException;
29+
import java.sql.SQLFeatureNotSupportedException;
2930
import java.sql.Statement;
3031
import java.sql.Types;
31-
3232
import javax.sql.DataSource;
3333

3434
import org.apache.commons.logging.Log;
3535
import org.apache.commons.logging.LogFactory;
3636

3737
import org.springframework.jdbc.CannotGetJdbcConnectionException;
3838
import org.springframework.jdbc.datasource.DataSourceUtils;
39+
import org.springframework.util.ClassUtils;
3940

4041
/**
4142
* Generic utility methods for working with JDBC. Mainly for internal use
@@ -53,6 +54,10 @@ public abstract class JdbcUtils {
5354
public static final int TYPE_UNKNOWN = Integer.MIN_VALUE;
5455

5556

57+
// Check for JDBC 4.1 getObject(int, Class) method - available on JDK 7 and higher
58+
private static final boolean getObjectWithTypeAvailable =
59+
ClassUtils.hasMethod(ResultSet.class, "getObject", int.class, Class.class);
60+
5661
private static final Log logger = LogFactory.getLog(JdbcUtils.class);
5762

5863

@@ -134,74 +139,74 @@ public static Object getResultSetValue(ResultSet rs, int index, Class<?> require
134139
return getResultSetValue(rs, index);
135140
}
136141

137-
Object value = null;
138-
boolean wasNullCheck = false;
142+
Object value;
139143

140144
// Explicitly extract typed value, as far as possible.
141145
if (String.class.equals(requiredType)) {
142-
value = rs.getString(index);
146+
return rs.getString(index);
143147
}
144148
else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {
145149
value = rs.getBoolean(index);
146-
wasNullCheck = true;
147150
}
148151
else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) {
149152
value = rs.getByte(index);
150-
wasNullCheck = true;
151153
}
152154
else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) {
153155
value = rs.getShort(index);
154-
wasNullCheck = true;
155156
}
156157
else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) {
157158
value = rs.getInt(index);
158-
wasNullCheck = true;
159159
}
160160
else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) {
161161
value = rs.getLong(index);
162-
wasNullCheck = true;
163162
}
164163
else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {
165164
value = rs.getFloat(index);
166-
wasNullCheck = true;
167165
}
168166
else if (double.class.equals(requiredType) || Double.class.equals(requiredType) ||
169167
Number.class.equals(requiredType)) {
170168
value = rs.getDouble(index);
171-
wasNullCheck = true;
172169
}
173-
else if (byte[].class.equals(requiredType)) {
174-
value = rs.getBytes(index);
170+
else if (BigDecimal.class.equals(requiredType)) {
171+
return rs.getBigDecimal(index);
175172
}
176173
else if (java.sql.Date.class.equals(requiredType)) {
177-
value = rs.getDate(index);
174+
return rs.getDate(index);
178175
}
179176
else if (java.sql.Time.class.equals(requiredType)) {
180-
value = rs.getTime(index);
177+
return rs.getTime(index);
181178
}
182179
else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
183-
value = rs.getTimestamp(index);
180+
return rs.getTimestamp(index);
184181
}
185-
else if (BigDecimal.class.equals(requiredType)) {
186-
value = rs.getBigDecimal(index);
182+
else if (byte[].class.equals(requiredType)) {
183+
return rs.getBytes(index);
187184
}
188185
else if (Blob.class.equals(requiredType)) {
189-
value = rs.getBlob(index);
186+
return rs.getBlob(index);
190187
}
191188
else if (Clob.class.equals(requiredType)) {
192-
value = rs.getClob(index);
189+
return rs.getClob(index);
193190
}
194191
else {
195192
// Some unknown type desired -> rely on getObject.
196-
value = getResultSetValue(rs, index);
193+
if (getObjectWithTypeAvailable) {
194+
try {
195+
return rs.getObject(index, requiredType);
196+
}
197+
catch (SQLFeatureNotSupportedException ex) {
198+
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
199+
}
200+
catch (AbstractMethodError err) {
201+
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
202+
}
203+
}
204+
// Fall back to getObject without type specification...
205+
return getResultSetValue(rs, index);
197206
}
198207

199-
// Perform was-null check if demanded (for results that the
200-
// JDBC driver returns as primitives).
201-
if (wasNullCheck && value != null && rs.wasNull()) {
202-
value = null;
203-
}
204-
return value;
208+
// Perform was-null check if necessary (for results that the JDBC driver returns as primitives).
209+
return (rs.wasNull() ? null : value);
205210
}
206211

207212
/**
@@ -234,15 +239,12 @@ public static Object getResultSetValue(ResultSet rs, int index) throws SQLExcept
234239
else if (obj instanceof Clob) {
235240
obj = rs.getString(index);
236241
}
237-
else if (className != null &&
238-
("oracle.sql.TIMESTAMP".equals(className) ||
239-
"oracle.sql.TIMESTAMPTZ".equals(className))) {
242+
else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) {
240243
obj = rs.getTimestamp(index);
241244
}
242245
else if (className != null && className.startsWith("oracle.sql.DATE")) {
243246
String metaDataClassName = rs.getMetaData().getColumnClassName(index);
244-
if ("java.sql.Timestamp".equals(metaDataClassName) ||
245-
"oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
247+
if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
246248
obj = rs.getTimestamp(index);
247249
}
248250
else {
@@ -371,9 +373,6 @@ public static boolean supportsBatchUpdates(Connection con) {
371373
catch (SQLException ex) {
372374
logger.debug("JDBC driver 'supportsBatchUpdates' method threw exception", ex);
373375
}
374-
catch (AbstractMethodError err) {
375-
logger.debug("JDBC driver does not support JDBC 2.0 'supportsBatchUpdates' method", err);
376-
}
377376
return false;
378377
}
379378

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -26,13 +26,13 @@
2626
import java.sql.Statement;
2727
import java.util.List;
2828
import java.util.Map;
29-
3029
import javax.sql.DataSource;
3130

3231
import org.junit.Before;
3332
import org.junit.Rule;
3433
import org.junit.Test;
3534
import org.junit.rules.ExpectedException;
35+
3636
import org.springframework.dao.IncorrectResultSizeDataAccessException;
3737

3838
import static org.junit.Assert.*;
@@ -177,7 +177,7 @@ public void testQueryForObjectWithString() throws Exception {
177177
public void testQueryForObjectWithBigInteger() throws Exception {
178178
String sql = "SELECT AGE FROM CUSTMR WHERE ID = 3";
179179
given(this.resultSet.next()).willReturn(true, false);
180-
given(this.resultSet.getObject(1)).willReturn("22");
180+
given(this.resultSet.getObject(1, BigInteger.class)).willReturn(new BigInteger("22"));
181181
assertEquals(new BigInteger("22"), this.template.queryForObject(sql, BigInteger.class));
182182
verify(this.resultSet).close();
183183
verify(this.statement).close();

0 commit comments

Comments
 (0)