Tomcat 9 带 Apache DBCP + Spring 5 + Oracle 12c + SqlArrayValue
Tomcat 9 w/ Apache DBCP + Spring 5 + Oracle 12c + SqlArrayValue
我们现有的 SpringMVC J2EE 应用程序在 Orale WebLogic 上运行,我们正在尝试将其迁移到 Apache Tomcat。一切似乎都正常,除了我们在通过 JDBC 将数组值绑定到数据库时遇到问题。下面是如何在 WebLogic 上成功完成的。
SqlParameterSource in = new MapSqlParameterSource()
.addValue("i_username", user.getUsername())
.addValue("i_statuses",
new SqlArrayValue<String>(statuses,
"VARCHAR_TABLE_T"));
Map<String, Object> out = myDatabaseProc.execute(in);
statuses 变量是一个字符串数组,VARCHAR_TABLE_T 是相同的 Oracle DB 类型。 SqlArrayValue 是 Spring class 专门用于 JDBC.
中的 Oracle 数组处理
执行代码时出现以下错误(同样,这仅在 Tomcat 上出现错误)。
java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection
at oracle.sql.TypeDescriptor.setPhysicalConnectionOf(TypeDescriptor.java:803) ~[ojdbc8.jar:12.2.0.1.0]
at oracle.sql.TypeDescriptor.<init>(TypeDescriptor.java:585) ~[ojdbc8.jar:12.2.0.1.0]
at oracle.sql.ArrayDescriptor.<init>(ArrayDescriptor.java:258) ~[ojdbc8.jar:12.2.0.1.0]
at org.springframework.data.jdbc.support.oracle.SqlArrayValue.createTypeValue(SqlArrayValue.java:90) ~[spring-data-oracle-1.2.1.RELEASE.jar:?]
at org.springframework.jdbc.core.support.AbstractSqlTypeValue.setTypeValue(AbstractSqlTypeValue.java:60) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:293) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:232) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:147) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1090) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1147) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:412) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:372) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
我在网上发现了一堆关于解开连接以执行供应商特定操作的信息,但是 Spring 似乎应该为我做这些?我尝试在 Tomcat 的 server.xml 中将 accessToUnderlyingConnectionAllowed 设置为 true(我在网上找到的东西),我们在那里设置了连接资源,但这并不影响行为。
这是来自 server.xml 的资源。
<Resource name="jdbc/datasource" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@//db.domain.com:port/sid"
username="user" password="password"
maxTotal="15" maxIdle="3" maxWaitMillis="-1"
accessToUnderlyingConnectionAllowed="true" />
spring-data-jdbc-ext 似乎停产了。 SqlArrayValue class 只是没有展开连接。我最终放弃了 JDBC 扩展并推出了我自己非常相似的 class 来处理解包连接。这现在适用于 WebLogic 和 Tomcat(将 accessToUnderlyingConnectionAllowed 设置为 true)DBCP 和 Tomcat 的本机连接池(不需要 accessToUnderlyingConnectionAllowed)。
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
import oracle.jdbc.OracleConnection;
public class OracleSqlArrayValue<T> extends AbstractSqlTypeValue {
private T[] values;
private String defaultTypeName;
public OracleSqlArrayValue(T[] values) {
this.values = values;
}
public OracleSqlArrayValue(T[] values, String defaultTypeName) {
this.values = values;
this.defaultTypeName = defaultTypeName;
}
@Override
protected Object createTypeValue(Connection conn, int sqlType,
String typeName) throws SQLException {
if (typeName == null && defaultTypeName == null) {
throw new InvalidDataAccessApiUsageException(
"No type named defined. Instantiate class with default type name.");
}
if (!conn.isWrapperFor(OracleConnection.class)) {
throw new InvalidDataAccessApiUsageException(
"Unable to unwrap OracleConnection. Ensure you are connecting to an Oracle DB.");
}
return conn.unwrap(OracleConnection.class).createOracleArray(
typeName != null ? typeName : defaultTypeName, values);
}
}
我们现有的 SpringMVC J2EE 应用程序在 Orale WebLogic 上运行,我们正在尝试将其迁移到 Apache Tomcat。一切似乎都正常,除了我们在通过 JDBC 将数组值绑定到数据库时遇到问题。下面是如何在 WebLogic 上成功完成的。
SqlParameterSource in = new MapSqlParameterSource()
.addValue("i_username", user.getUsername())
.addValue("i_statuses",
new SqlArrayValue<String>(statuses,
"VARCHAR_TABLE_T"));
Map<String, Object> out = myDatabaseProc.execute(in);
statuses 变量是一个字符串数组,VARCHAR_TABLE_T 是相同的 Oracle DB 类型。 SqlArrayValue 是 Spring class 专门用于 JDBC.
中的 Oracle 数组处理执行代码时出现以下错误(同样,这仅在 Tomcat 上出现错误)。
java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection
at oracle.sql.TypeDescriptor.setPhysicalConnectionOf(TypeDescriptor.java:803) ~[ojdbc8.jar:12.2.0.1.0]
at oracle.sql.TypeDescriptor.<init>(TypeDescriptor.java:585) ~[ojdbc8.jar:12.2.0.1.0]
at oracle.sql.ArrayDescriptor.<init>(ArrayDescriptor.java:258) ~[ojdbc8.jar:12.2.0.1.0]
at org.springframework.data.jdbc.support.oracle.SqlArrayValue.createTypeValue(SqlArrayValue.java:90) ~[spring-data-oracle-1.2.1.RELEASE.jar:?]
at org.springframework.jdbc.core.support.AbstractSqlTypeValue.setTypeValue(AbstractSqlTypeValue.java:60) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:293) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:232) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:147) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1090) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1147) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:412) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:372) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198) ~[spring-jdbc-5.2.3.RELEASE.jar:5.2.3.RELEASE]
我在网上发现了一堆关于解开连接以执行供应商特定操作的信息,但是 Spring 似乎应该为我做这些?我尝试在 Tomcat 的 server.xml 中将 accessToUnderlyingConnectionAllowed 设置为 true(我在网上找到的东西),我们在那里设置了连接资源,但这并不影响行为。
这是来自 server.xml 的资源。
<Resource name="jdbc/datasource" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@//db.domain.com:port/sid"
username="user" password="password"
maxTotal="15" maxIdle="3" maxWaitMillis="-1"
accessToUnderlyingConnectionAllowed="true" />
spring-data-jdbc-ext 似乎停产了。 SqlArrayValue class 只是没有展开连接。我最终放弃了 JDBC 扩展并推出了我自己非常相似的 class 来处理解包连接。这现在适用于 WebLogic 和 Tomcat(将 accessToUnderlyingConnectionAllowed 设置为 true)DBCP 和 Tomcat 的本机连接池(不需要 accessToUnderlyingConnectionAllowed)。
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
import oracle.jdbc.OracleConnection;
public class OracleSqlArrayValue<T> extends AbstractSqlTypeValue {
private T[] values;
private String defaultTypeName;
public OracleSqlArrayValue(T[] values) {
this.values = values;
}
public OracleSqlArrayValue(T[] values, String defaultTypeName) {
this.values = values;
this.defaultTypeName = defaultTypeName;
}
@Override
protected Object createTypeValue(Connection conn, int sqlType,
String typeName) throws SQLException {
if (typeName == null && defaultTypeName == null) {
throw new InvalidDataAccessApiUsageException(
"No type named defined. Instantiate class with default type name.");
}
if (!conn.isWrapperFor(OracleConnection.class)) {
throw new InvalidDataAccessApiUsageException(
"Unable to unwrap OracleConnection. Ensure you are connecting to an Oracle DB.");
}
return conn.unwrap(OracleConnection.class).createOracleArray(
typeName != null ? typeName : defaultTypeName, values);
}
}