Browse Source

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
pull/512/head
Juergen Hoeller 12 years ago
parent
commit
37679384e8
  1. 18
      spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java
  2. 6
      spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java
  3. 14
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/JdbcTransactionObjectSupport.java
  4. 71
      spring-jdbc/src/main/java/org/springframework/jdbc/support/JdbcUtils.java
  5. 6
      spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateQueryTests.java

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,7 +30,6 @@ import java.util.List; @@ -30,7 +30,6 @@ import java.util.List;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
import org.springframework.util.Assert;
@ -228,18 +227,11 @@ public class PreparedStatementCreatorFactory { @@ -228,18 +227,11 @@ public class PreparedStatementCreatorFactory {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps;
if (generatedKeysColumnNames != null || returnGeneratedKeys) {
try {
if (generatedKeysColumnNames != null) {
ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
}
else {
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
}
if (generatedKeysColumnNames != null) {
ps = con.prepareStatement(this.actualSql, generatedKeysColumnNames);
}
catch (AbstractMethodError err) {
throw new InvalidDataAccessResourceUsageException(
"Your JDBC driver is not compliant with JDBC 3.0 - " +
"it does not support retrieval of auto-generated keys", err);
else {
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
}
}
else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {

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

@ -87,10 +87,8 @@ public abstract class StatementCreatorUtils { @@ -87,10 +87,8 @@ public abstract class StatementCreatorUtils {
private static final Map<Class<?>, Integer> javaTypeToSqlTypeMap = new HashMap<Class<?>, Integer>(32);
static {
/* JDBC 3.0 only - not compatible with e.g. MySQL at present
javaTypeToSqlTypeMap.put(boolean.class, new Integer(Types.BOOLEAN));
javaTypeToSqlTypeMap.put(Boolean.class, new Integer(Types.BOOLEAN));
*/
javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN);
javaTypeToSqlTypeMap.put(Boolean.class, Types.BOOLEAN);
javaTypeToSqlTypeMap.put(byte.class, Types.TINYINT);
javaTypeToSqlTypeMap.put(Byte.class, Types.TINYINT);
javaTypeToSqlTypeMap.put(short.class, Types.SMALLINT);

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.jdbc.datasource;
import java.sql.SQLException;
import java.sql.Savepoint;
import org.apache.commons.logging.Log;
@ -41,9 +42,6 @@ import org.springframework.transaction.support.SmartTransactionObject; @@ -41,9 +42,6 @@ import org.springframework.transaction.support.SmartTransactionObject;
* will automatically delegate to this, as it autodetects transaction
* objects that implement the SavepointManager interface.
*
* <p>Note that savepoints are only supported for drivers which
* support JDBC 3.0 or higher.
*
* @author Juergen Hoeller
* @since 1.1
*/
@ -109,15 +107,9 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager, @@ -109,15 +107,9 @@ public abstract class JdbcTransactionObjectSupport implements SavepointManager,
throw new NestedTransactionNotSupportedException(
"Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
}
}
catch (Throwable ex) {
throw new NestedTransactionNotSupportedException(
"Cannot create a nested transaction because your JDBC driver is not a JDBC 3.0 driver", ex);
}
try {
return conHolder.createSavepoint();
}
catch (Throwable ex) {
catch (SQLException ex) {
throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
}
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,9 +26,9 @@ import java.sql.DatabaseMetaData; @@ -26,9 +26,9 @@ import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.sql.Types;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
@ -36,6 +36,7 @@ import org.apache.commons.logging.LogFactory; @@ -36,6 +36,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.util.ClassUtils;
/**
* Generic utility methods for working with JDBC. Mainly for internal use
@ -53,6 +54,10 @@ public abstract class JdbcUtils { @@ -53,6 +54,10 @@ public abstract class JdbcUtils {
public static final int TYPE_UNKNOWN = Integer.MIN_VALUE;
// Check for JDBC 4.1 getObject(int, Class) method - available on JDK 7 and higher
private static final boolean getObjectWithTypeAvailable =
ClassUtils.hasMethod(ResultSet.class, "getObject", int.class, Class.class);
private static final Log logger = LogFactory.getLog(JdbcUtils.class);
@ -134,74 +139,74 @@ public abstract class JdbcUtils { @@ -134,74 +139,74 @@ public abstract class JdbcUtils {
return getResultSetValue(rs, index);
}
Object value = null;
boolean wasNullCheck = false;
Object value;
// Explicitly extract typed value, as far as possible.
if (String.class.equals(requiredType)) {
value = rs.getString(index);
return rs.getString(index);
}
else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {
value = rs.getBoolean(index);
wasNullCheck = true;
}
else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) {
value = rs.getByte(index);
wasNullCheck = true;
}
else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) {
value = rs.getShort(index);
wasNullCheck = true;
}
else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) {
value = rs.getInt(index);
wasNullCheck = true;
}
else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) {
value = rs.getLong(index);
wasNullCheck = true;
}
else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {
value = rs.getFloat(index);
wasNullCheck = true;
}
else if (double.class.equals(requiredType) || Double.class.equals(requiredType) ||
Number.class.equals(requiredType)) {
value = rs.getDouble(index);
wasNullCheck = true;
}
else if (byte[].class.equals(requiredType)) {
value = rs.getBytes(index);
else if (BigDecimal.class.equals(requiredType)) {
return rs.getBigDecimal(index);
}
else if (java.sql.Date.class.equals(requiredType)) {
value = rs.getDate(index);
return rs.getDate(index);
}
else if (java.sql.Time.class.equals(requiredType)) {
value = rs.getTime(index);
return rs.getTime(index);
}
else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
value = rs.getTimestamp(index);
return rs.getTimestamp(index);
}
else if (BigDecimal.class.equals(requiredType)) {
value = rs.getBigDecimal(index);
else if (byte[].class.equals(requiredType)) {
return rs.getBytes(index);
}
else if (Blob.class.equals(requiredType)) {
value = rs.getBlob(index);
return rs.getBlob(index);
}
else if (Clob.class.equals(requiredType)) {
value = rs.getClob(index);
return rs.getClob(index);
}
else {
// Some unknown type desired -> rely on getObject.
value = getResultSetValue(rs, index);
if (getObjectWithTypeAvailable) {
try {
return rs.getObject(index, requiredType);
}
catch (SQLFeatureNotSupportedException ex) {
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
}
catch (AbstractMethodError err) {
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
}
}
// Fall back to getObject without type specification...
return getResultSetValue(rs, index);
}
// Perform was-null check if demanded (for results that the
// JDBC driver returns as primitives).
if (wasNullCheck && value != null && rs.wasNull()) {
value = null;
}
return value;
// Perform was-null check if necessary (for results that the JDBC driver returns as primitives).
return (rs.wasNull() ? null : value);
}
/**
@ -234,15 +239,12 @@ public abstract class JdbcUtils { @@ -234,15 +239,12 @@ public abstract class JdbcUtils {
else if (obj instanceof Clob) {
obj = rs.getString(index);
}
else if (className != null &&
("oracle.sql.TIMESTAMP".equals(className) ||
"oracle.sql.TIMESTAMPTZ".equals(className))) {
else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) {
obj = rs.getTimestamp(index);
}
else if (className != null && className.startsWith("oracle.sql.DATE")) {
String metaDataClassName = rs.getMetaData().getColumnClassName(index);
if ("java.sql.Timestamp".equals(metaDataClassName) ||
"oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
obj = rs.getTimestamp(index);
}
else {
@ -371,9 +373,6 @@ public abstract class JdbcUtils { @@ -371,9 +373,6 @@ public abstract class JdbcUtils {
catch (SQLException ex) {
logger.debug("JDBC driver 'supportsBatchUpdates' method threw exception", ex);
}
catch (AbstractMethodError err) {
logger.debug("JDBC driver does not support JDBC 2.0 'supportsBatchUpdates' method", err);
}
return false;
}

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,13 +26,13 @@ import java.sql.SQLException; @@ -26,13 +26,13 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import static org.junit.Assert.*;
@ -177,7 +177,7 @@ public class JdbcTemplateQueryTests { @@ -177,7 +177,7 @@ public class JdbcTemplateQueryTests {
public void testQueryForObjectWithBigInteger() throws Exception {
String sql = "SELECT AGE FROM CUSTMR WHERE ID = 3";
given(this.resultSet.next()).willReturn(true, false);
given(this.resultSet.getObject(1)).willReturn("22");
given(this.resultSet.getObject(1, BigInteger.class)).willReturn(new BigInteger("22"));
assertEquals(new BigInteger("22"), this.template.queryForObject(sql, BigInteger.class));
verify(this.resultSet).close();
verify(this.statement).close();

Loading…
Cancel
Save