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
This commit is contained in:
Juergen Hoeller
2014-04-10 15:51:44 +02:00
parent 1bbc55d63c
commit 37679384e8
5 changed files with 54 additions and 73 deletions

View File

@@ -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;
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 {
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) {

View File

@@ -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);

View File

@@ -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 @@
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;
* 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,
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);
}
}

View File

@@ -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;
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;
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 {
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 {
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 (java.sql.Date.class.equals(requiredType)) {
value = rs.getDate(index);
}
else if (java.sql.Time.class.equals(requiredType)) {
value = rs.getTime(index);
}
else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
value = rs.getTimestamp(index);
}
else if (BigDecimal.class.equals(requiredType)) {
value = rs.getBigDecimal(index);
return rs.getBigDecimal(index);
}
else if (java.sql.Date.class.equals(requiredType)) {
return rs.getDate(index);
}
else if (java.sql.Time.class.equals(requiredType)) {
return rs.getTime(index);
}
else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
return rs.getTimestamp(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 {
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 {
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;
}