Tomcat 9 с 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);

Переменная статусов представляет собой массив String, а VARCHAR_TABLE_T - это тот же тип базы данных Oracle. SqlArrayValue - это класс Spring, специально предназначенный для обработки массивов Oracle в JDBC.

Когда код выполняется, мы получаем следующую ошибку (опять же, это не работает только на 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 должен делать это за меня? Я попытался установить для accessToUnderlyingConnectionAllowed значение true в Tomcat server.xml (что-то, что я нашел в Интернете), где у нас есть настройка ресурса подключения, но это не повлияло на поведение.

Вот ресурс из 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" />

1 ответ

Решение

Похоже, что spring-data-jdbc-ext является EOL. Класс SqlArrayValue просто не разворачивает соединение. В итоге я отказался от расширений JDBC и развернул свой собственный очень похожий класс, который обрабатывает развертывание соединения. Теперь это работает в 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);
    }
}
Другие вопросы по тегам