JDBC 超时不抛出 SQLTimeoutException

Posted

技术标签:

【中文标题】JDBC 超时不抛出 SQLTimeoutException【英文标题】:JDBC timeout is not throwing SQLTimeoutException 【发布时间】:2014-09-30 22:04:39 【问题描述】:

我正在尝试在 JDBC 语句上设置查询超时,并希望它在超时时抛出 SQLTimeoutException。 但相反,我得到了一个带有错误代码 ORA-01013 的通用 SQLException。

知道我必须错过什么吗?

我正在寻找一种独立于数据库的方式来处理超时。并且检查特定于数据库的错误代码可能无济于事。

顺便说一句,我正在通过 Spring 的 JdbcTemplate 设置此属性。

myStatement.setQueryTimeout(1);

抛出

java.sql.SQLException: ORA-01013: user requested cancel of current operation

编辑:这是错误的堆栈跟踪。我正在使用瘦驱动程序(ojdbc6-11.2.0.1.0.jar)。

java.sql.SQLException: ORA-01013: user requested cancel of current operation

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:861)
    at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1145)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1259)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449)
    at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3493)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1491)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:703)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:644)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:695)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:722)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:772)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:211)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:219)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(NamedParameterJdbcTemplate.java:233)
    at mytest.MyDAO.retrieve(MyDAO.java:12)

【问题讨论】:

证据?堆栈跟踪? 【参考方案1】:

这是驱动程序用来抛出异常/错误的代码,它不会将异常映射到 SQLTimeoutException。这是 oracle 驱动程序实现中的差距

参考 https://github.com/wenshao/OracleDriver10_2_0_2/blob/4af5a9295e0e9fef3f7e51ba7bf735fb81e9186a/src/oracle/jdbc/driver/T4CTTIoer.java

从下面完整方法中获取的sn-p是

     DatabaseError.throwSqlException(this.meg.conv
                    .CharBytesToString(this.errorMsg, this.errorLength[0],
                            true), DatabaseError
                    .ErrorToSQLState(this.retCode), this.retCode);

完整的方法是这样的

/*     */
/*     */void processError(boolean paramBoolean,
        OracleStatement paramOracleStatement)
/*     */throws SQLException
/*     */
    /* 303 */if (this.retCode != 0)
    /*     */
        /* 311 */switch (this.retCode)
        /*     */
        /*     */case 28:
            /*     */
        case 600:
            /*     */
        case 1012:
            /*     */
        case 3113:
            /*     */
        case 3114:
            /* 323 */this.connection.internalClose();
            /*     */
        /*     */
        /* 328 */if (paramBoolean)
        /*     */
            **/* 331 */
             *DatabaseError.throwSqlException(this.meg.conv
                    .CharBytesToString(this.errorMsg, this.errorLength[0],
                            true), DatabaseError
                    .ErrorToSQLState(this.retCode), this.retCode);*
            /*     */
        /*     */else
        /*     */
            /* 335 */return;
            /*     */
        /*     */
        /*     */
    /*     */
    /* 341 */if (!paramBoolean) 
        /* 342 */return;
        /*     */
    /*     */
    /* 351 */if ((this.warningFlag & 0x1) == 1)
    /*     */
        /* 353 */int i = this.warningFlag & 0xFFFFFFFE;
        /*     */
        /* 356 */if (((i & 0x20) == 32) || ((i & 0x4) == 4)) 
            /* 357 */throw DatabaseError.newSqlException(110);
            /*     */
        /*     */
    /*     */
    /* 361 */if ((this.connection != null)
            && (this.connection.plsqlCompilerWarnings))
    /*     */
        /* 363 */if ((this.flags & 0x4) == 4)
            /* 364 */paramOracleStatement.foundPlsqlCompilerWarning();
        /*     */
    /*     */

【讨论】:

谢谢桑迪普。我想那我不得不自己用肮脏的方式来检查错误代码。【参考方案2】:

不幸的是,JDBC 驱动程序并不总是实现 JDBC 所需(或:建议)的所有内容。这尤其适用于“较新”的功能。在 Java 6 / JDBC 4 中添加了SQLTimeoutException

这有两个含义:

    它在旧版本中不可用, 您需要适用于 JDBC 4 或更高版本的驱动程序。

还有更复杂的情况:

    鉴于您问题中的特定错误消息(“ORA-01013:用户请求取消当前操作”),驱动程序可能根本无法识别实际用户发起的操作从一个由超时启动的语句取消。在这种情况下,最好抛出最通用的异常类型。

    如果供应商支持多个 Java 和 JDBC 版本,他们可能只是走捷径,只做最低限度的(几乎)JDBC 兼容。这可能只包括抛出 SQLException 以使他们更轻松地使用相同的代码库而无需大惊小怪。

【讨论】:

以上是关于JDBC 超时不抛出 SQLTimeoutException的主要内容,如果未能解决你的问题,请参考以下文章

一种设置python函数执行超时时间 | 函数超时后不抛出异常的方法

为啥这段代码不抛出 NullPointerException?

为啥设置 DataSource 时 ComboBox 不抛出异常?

为啥 JPA 重复持久方法不抛出异常?

VBScript 上的错误处理:不抛出错误

为啥不抛出异常的代码允许捕获已检查的异常?