Spring 自定义 SQL 错误代码转换器未捕获连接异常

Spring custom SQL error code translator not catching connection exception

使用 Spring JdbcTemplate 与 HikariCP 和 JTDS 连接到我正在维护的遗留代码中的 MS SQL 服务器数据库。我正在尝试处理应用程序已启动的情况,以及后来由于某种原因数据库变得不可用并引发连接异常的情况。

我已经有一个自定义的 SQL 错误代码转换器(如下),它可以捕获 jdbcTemplate 抛出的许多其他异常。但是连接被拒绝的异常没有被捕获,即使它被封装为 java.sql.SQLException (参见堆栈跟踪)。

为什么翻译器没有捕捉到这个异常?

public class CustomSqlErrorCodeTranslator extends 
                    SQLErrorCodeSQLExceptionTranslator   {

    private final Log log = LogFactory.getLog(getClass());


    @Override
    protected DataAccessException customTranslate
    (String task, String sql, SQLException sqlException) {
        /* 
         * We want to capture all codes.      
         */
            StringBuilder logMsg = new StringBuilder("*********************SQL Exception:");
            logMsg.append(", errorCode: " + sqlException.getErrorCode())
                  .append(", message: " + sqlException.getMessage())
                  ;
            log.error(logMsg.toString());


        return null;
    }



}
public class Dao {

    //HikariDataSource dataSource;
    DataSource dataSource;

    protected JdbcTemplate jdbcTemplate; 
    private final Log log = LogFactory.getLog(getClass());
    private boolean connected = false;
    private CustomSqlErrorCodeTranslator exceptionHandler;

    public void setDataSource(DataSource ds) {
    //this.jdbcTemplate = new JdbcTemplate(ds);


        try {
            this.jdbcTemplate = new JdbcTemplate(ds);
            exceptionHandler = new CustomSqlErrorCodeTranslator();
            this.jdbcTemplate.setExceptionTranslator(exceptionHandler);
            connected = true;

        } catch (Exception e) {
            if (e.getCause() != null && e.getCause() instanceof SQLException) {
                log.error("******Cannot connect to database.***************************");
                log.debug(e.getMessage());


            }

        }
    }

}
16:59:33,542 [main] WARN  PoolBase  - springHikariCP - Connection net.sourceforge.jtds.jdbc.JtdsConnection@42206bce failed alive test with exception I/O Error: Connection reset by peer: socket write error
16:59:33,542 [Hikari connection closer (pool springHikariCP)] DEBUG PoolBase  - springHikariCP - Closing connection net.sourceforge.jtds.jdbc.JtdsConnection@42206bce: (connection evicted or dead)
16:59:34,530 [main] DEBUG HikariPool  - Timeout failure pool springHikariCP stats (total=0, active=0, idle=0, waiting=0)
16:59:34,544 [Hikari connection adder (pool springHikariCP)] DEBUG HikariPool  - springHikariCP - Cannot acquire connection from data source
java.sql.SQLException: Network error IOException: Connection refused: connect
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:436)
    at net.sourceforge.jtds.jdbc.Driver.connect(Driver.java:184)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:95)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:101)
    at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:314)
    at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:171)
    at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:436)
    at com.zaxxer.hikari.pool.HikariPool.access0(HikariPool.java:65)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:567)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:560)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at net.sourceforge.jtds.jdbc.SharedSocket.createSocketForJDBC3(SharedSocket.java:288)
    at net.sourceforge.jtds.jdbc.SharedSocket.<init>(SharedSocket.java:251)
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:331)
    ... 13 more
16:59:35,746 [Hikari connection adder (pool springHikariCP)] DEBUG HikariPool  - springHikariCP - Cannot acquire connection from data source
java.sql.SQLException: Network error IOException: Connection refused: connect
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:436)
    at net.sourceforge.jtds.jdbc.Driver.connect(Driver.java:184)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:95)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:101)
    at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:314)
    at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:171)
    at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:436)
    at com.zaxxer.hikari.pool.HikariPool.access0(HikariPool.java:65)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:567)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:560)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at net.sourceforge.jtds.jdbc.SharedSocket.createSocketForJDBC3(SharedSocket.java:288)
    at net.sourceforge.jtds.jdbc.SharedSocket.<init>(SharedSocket.java:251)
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:331)
    ... 13 more
16:59:37,010 [Hikari connection adder (pool springHikariCP)] DEBUG HikariPool  - springHikariCP - Cannot acquire connection from data source

抱歉,我花了这么长时间才意识到我需要添加到堆栈跟踪中。

这是我停止数据库时的堆栈跟踪。现在很明显涉及到JDBC模板。如果涉及 JdbcTemplate,在我看来,自定义异常处理程序应该将其拾取。有没有其他人有使用自定义错误代码转换器捕获数据库连接异常的经验?

5:06:05,076 [ActiveMQ Session Task-1] DEBUG HikariPool  - Timeout failure   pool HikariPool-0 stats (total=1, active=1, idle=0, waiting=2)
org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-0 - Connection is not available, request timed out after 10000ms.
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:615)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:866)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:927)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:937)
    at com.pevco.persist.resources.AvailabilityDaoImpl.create(AvailabilityDaoImpl.java:51)
    at com.pevco.nexgen.health.handlers.SystemModelListener.onMessage(SystemModelListener.java:135)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:746)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:684)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:622)
    at org.springframework.jms.listener.SimpleMessageListenerContainer.processMessage(SimpleMessageListenerContainer.java:330)
    at org.springframework.jms.listener.SimpleMessageListenerContainer.onMessage(SimpleMessageListenerContainer.java:306)
    at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1321)
    at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131)
    at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202)
    at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:129)
    at org.apache.activemq.thread.PooledTaskRunner.run(PooledTaskRunner.java:47)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.sql.SQLTransientConnectionException: HikariPool-0 - Connection is not available, request timed out after 10000ms.
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:195)
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147)
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:83)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    ... 20 more
Caused by: java.sql.SQLException: Network error IOException: Connection refused: connect
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:436)
    at net.sourceforge.jtds.jdbc.Driver.connect(Driver.java:184)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:95)
    at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:314)
    at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:171)
    at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:436)
    at com.zaxxer.hikari.pool.HikariPool.access0(HikariPool.java:65)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:567)
    at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:560)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    ... 3 more
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at net.sourceforge.jtds.jdbc.SharedSocket.createSocketForJDBC3(SharedSocket.java:288)
    at net.sourceforge.jtds.jdbc.SharedSocket.<init>(SharedSocket.java:251)
    at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:331)
    ... 12 more
15:06:06,217 [ActiveMQ Session Task-1] DEBUG HikariPool  - Timeout failure  pool HikariPool-0 stats (total=1, active=1, idle=0, waiting=2)
15:06:06,217 [ActiveMQ Session Task-1] WARN  SimpleMessageListenerContainer  - Execution of JMS message listener failed, and no ErrorHandler has been set.

(我会在时间到期前奖励赏金,但希望得到准确的答案。)

问题是因为 Hikari 组件正在请求连接。可能 Hikari 失去了连接,它正在尝试重新连接。

日志中没有任何与JdbcTemplate 相关的部分。那是因为问题是由重新连接尝试引起的,而不是因为新事务。

如果事务未通过模板,则永远不会调用错误转换器。抛出异常的事件不会被业务逻辑触发。它由 Hikari

的重新连接或创建连接功能触发

正如 "reos" 所指出的,堆栈跟踪中没有任何与 JdbcTemplate 相关的内容。这意味着您试图在 JdbcTemplate 外部打开连接。

即使您使用了 JdbcTemplate,也不会翻译异常。看看execute method in the source code。如果数据源无法获得连接,他们不使用自定义异常转换器。