【问题标题】:Spring custom SQL error code translator not catching connection exceptionSpring 自定义 SQL 错误代码转换器未捕获连接异常
【发布时间】:2016-06-03 21:22:39
【问题描述】:

将 Spring JdbcTemplate 与 HikariCP 和 JTDS 一起使用,以我正在维护的旧代码中连接到 MS SQL Server 数据库。我正在尝试处理应用程序已启动的情况,后来无论出于何种原因数据库变得不可用并引发连接异常。

我已经有一个自定义的 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.access$500(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.access$500(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$2.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$1.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.access$500(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.

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

【问题讨论】:

  • 您是否将翻译器设置为 JdbcTemplate ?
  • 是的。我在发生这种情况的地方添加了道代码。当前代码检查 SqlException 的实例,但我也尝试过不使用任何 if 语句,当错误是数据库连接错误时,它仍然没有命中 catch 子句。我可以通过从表中删除一列来触发 catch 块。所以翻译器正在为其他 SqlExceptions 工作,而不是为未连接的数据库。
  • 可能错误是因为 hakiri os 试图重新连接。而不是因为你的模板有问题。
  • Hikari 在指定的时间内尝试重新连接,当前设置为 1 秒。如果超时,则会引发数据库连接错误,该错误会被 jdbc 模板捕获并作为 SqlException 重新引发。但是,异常翻译器不会捕获此异常。如果其他人在使用翻译器,我想知道如何捕获数据库连接错误和翻译器。
  • 在日志中我看不到模板涉及的位置。我认为它没有通过模板。模板不询问连接。它是由 hikari 制作的

标签: spring spring-jdbc hikaricp


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    如果事务没有通过模板传递,则永远不会调用错误翻译器。业务逻辑不会触发引发异常的事件。它由 Hikari 的重新连接或创建连接功能触发

    【讨论】:

    • 你是对的,因为我在测试过程中停止了数据库,所以 Hikari 确实失去了连接。我明白你的观点,Hikari 会尝试重新连接,但即使没有通过 JdbcTemplate 调用数据库,它会这样做吗? Hikari 是在春季 IOC 开始的。有什么办法可以捕捉到这个连接异常并处理它?
    • Hikari 是一个连接池,所以它启动时会创建一个连接池。当有新的调用时,JDBCTemplate 请求与 Hikari 的连接。 Hikari 在池中寻找可用的连接。就这样。这就是它的工作方式。在另一边,你有 Hikari 管理连接池,它会尝试保持一些打开的连接,如果打开的连接数减少到最小值以下,它将尝试重新连接该连接,这是自动的,它不是由 Hikari 启动的通过 JDBC 模板。如果你需要捕捉 Hikari 抛出的异常,你需要...
    • 如果你需要捕捉 Hikari 抛出的异常,你需要一个监听器或一个能够感知 Hikari 事件的类。在这种情况下,我不知道 Hikari 儿子的情况,我无法在课程或工作方式方面为您提供帮助。
    • 我希望我可以拆分赏金,因为@varad 将我指向 jdbcTemplate 代码。但是这里的其他人也做出了重要的 cmets,这让我更仔细地查看了堆栈跟踪。导致问题的堆栈跟踪是由我的一个 dao 生成的,甚至不是 JdbcTemplate。我之前没有注意到这一点,因为我没有创建那个 dao。感谢您对连接池操作的详细说明。那是最有帮助的!我通过修改奇怪的 dao 并记录错误而不是打印堆栈跟踪来解决我的问题。达哈! ;-)
    猜你喜欢
    • 2017-05-24
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    相关资源
    最近更新 更多