Spring 5需nativeJdbcExtractor时的JDBC替代方案咨询
解决Spring 5+升级后JdbcTemplate丢失nativeJdbcExtractor导致的Oracle连接强转异常
你的问题核心在于Spring 5移除了NativeJdbcExtractor相关支持,而Oracle的旧JDBC代码(比如TypeDescriptor.setPhysicalConnectionOf)直接将连接强转为OracleConnection,没有遵循JDBC 4.0标准的unwrap()方法,导致被commons-dbcp2的PoolGuardConnectionWrapper包装类挡住,抛出类型转换异常。下面是几个可行的解决方案:
方案1:自定义SqlArrayValue适配unwrap逻辑
因为异常触发点在SqlArrayValue调用Oracle ArrayDescriptor的环节,我们可以重写这个类,手动在创建类型值时unwrap连接:
import org.springframework.data.jdbc.support.oracle.SqlArrayValue; import oracle.jdbc.OracleConnection; import java.sql.Connection; import java.sql.SQLException; public class OracleUnwrappingSqlArrayValue extends SqlArrayValue { private final String oracleTypeName; public OracleUnwrappingSqlArrayValue(String typeName, Object[] values) { super(typeName, values); this.oracleTypeName = typeName; } @Override protected Object createTypeValue(Connection connection, int sqlType, String typeName) throws SQLException { // 先将连接unwrap为真实的OracleConnection OracleConnection oracleConn = connection.unwrap(OracleConnection.class); // 调用父类方法,传入真实连接 return super.createTypeValue(oracleConn, sqlType, this.oracleTypeName); } }
之后在你的业务代码中,用这个自定义类替换原来的SqlArrayValue即可,这样就能在创建数组参数时传入真实的Oracle连接,避免强转错误。
方案2:包装DataSource自动处理强转(全局生效)
如果你的代码中有多处Oracle强转逻辑,不想逐个修改,可以自定义一个DataSource包装类,让它返回的连接在被强转为OracleConnection时自动unwrap:
import org.apache.commons.dbcp2.BasicDataSource; import oracle.jdbc.OracleConnection; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; public class OracleUnwrappingDataSource extends BasicDataSource { @Override public Connection getConnection() throws SQLException { Connection poolConn = super.getConnection(); // 创建动态代理,处理OracleConnection的方法调用和强转 return (Connection) Proxy.newProxyInstance( Connection.class.getClassLoader(), new Class[]{Connection.class, OracleConnection.class}, new OracleConnectionInvocationHandler(poolConn) ); } private static class OracleConnectionInvocationHandler implements InvocationHandler { private final Connection targetConn; public OracleConnectionInvocationHandler(Connection targetConn) { this.targetConn = targetConn; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 如果是OracleConnection接口的方法,先unwrap再调用 if (OracleConnection.class.isAssignableFrom(method.getDeclaringClass())) { OracleConnection realOracleConn = targetConn.unwrap(OracleConnection.class); return method.invoke(realOracleConn, args); } // 其他JDBC标准方法直接调用原连接 return method.invoke(targetConn, args); } } }
在Spring配置中替换原有的BasicDataSource为这个OracleUnwrappingDataSource,所有通过该数据源获取的连接都会自动处理强转逻辑,无需修改业务代码。
额外说明
- Spring 5移除
NativeJdbcExtractor是遵循JDBC 4.0标准的做法,官方推荐使用Connection.unwrap(Class<T>)来获取底层厂商特定的连接实现。 - commons-dbcp2的
PoolGuardConnectionWrapper本身已经实现了unwrap()方法,只要正确调用就能拿到真实连接,问题的根源是Oracle旧代码未使用标准API而是直接强转。
内容的提问来源于stack exchange,提问作者JavaDude
相关产品推荐
相关产品推荐

