即使明确设置 PreparedStatement 也不会超时
Posted
技术标签:
【中文标题】即使明确设置 PreparedStatement 也不会超时【英文标题】:PreparedStatement won't ever timeout even if explicitly set 【发布时间】:2018-02-09 10:18:51 【问题描述】:我正在尝试模拟一个场景,即我的服务失去与数据库的连接并且无法通过阻止与 iptables 的连接来执行 INSERT
,但我无法使 executeQuery()
方法超时。
我所做的是像statement.setQueryTimeout(5)
那样为 PreparedStatement 设置超时。这是代码。
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://db-url/db");
config.setUsername("user");
config.setPassword("passwd");
config.setMaximumPoolSize(10);
config.setAutoCommit(false);
config.setConnectionTimeout(5000);
config.setDriverClassName("com.mysql.jdbc.Driver");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("autoReconnect", "true");
final HikariDataSource pool = new HikariDataSource(config);
final String query = "INSERT INTO xtable VALUES (?, ?, ?, ?, ?)";
try ( Connection connection = pool.getConnection() )
try ( PreparedStatement statement = connection.prepareStatement(query) )
// this is what I expect to work
statement.setQueryTimeout(5);
for ( Info info : infos )
statement.setString(1, info.getValue1());
statement.setString(2, info.getValue2());
statement.setString(3, info.getValue3());
statement.setString(4, info.getValue4());
statement.setString(5, info.getValue5());
try
System.out.println("Waiting");
Thread.sleep(5000);
// I use this sleep to ban the database url with iptables
// to simulate a disconnection
System.out.println("Waited");
catch ( InterruptedException e )
e.printStackTrace();
System.out.println("Before executeQuery");
statement.executeQuery();
// I assumed that this would timeout after 5 seconds
// But it never reaches the next System.out.print
System.out.println("After executeQuery");
System.out.println("Before commit");
connection.commit();
System.out.println("After commit");
catch ( SQLException e )
log.error("Couldn't execute query", e);
输出将是:
Waiting
Waited
Before executeQuery
然后它永远挂起......我该怎么做才能让它抛出异常?
【问题讨论】:
【参考方案1】:打电话给Connection.setNetworkTimeout()
试一试。
private final static Executor immediateExecutor = Runnable::run;
try ( Connection connection = pool.getConnection() )
int timeout = connection.getNetworkTimeout();
connection.setNetworkTimeout(immediateExecutor, TimeUnit.SECONDS.toMillis(5));
...
try (PreparedStatement...)
...
finally
connection.setNetworkTimeout(timeout);
finally
...
您遇到了未经确认的 TCP 流量,如果未设置网络超时,可能会挂起连接。
【讨论】:
它似乎工作得很好,谢谢!有两件事困扰我:一,在 connection.close() 中,它抛出未捕获的异常,因为发生网络超时后无法关闭它。另外:我每次都必须设置这个超时吗?如果我有一个 Hikari 池,那么每次需要连接时都设置它似乎是无稽之谈,对吧?我的意思是,主要是因为我会继续创建执行器。 另一个选项是在驱动程序级别设置网络超时 - 全局。在这种情况下,我建议将其设置为最长查询时间的 3 倍。 最后一点。您不需要继续创建 Executors。如上面的示例,您可以(应该)创建一个静态 Executor,并且该 executor(在 MySQL 的情况下)应该立即执行 Runnable,而不是排队或传递给另一个线程。 好建议,我会试试的。我无法在 jdbc 驱动程序级别设置网络超时。谢谢【参考方案2】:你总是可以:
try
preparedstatement = connection.prepareStatement(query);
preparedstatement.setQueryTimeout(seconds);
而不是你所做的。也许这样会更好。 同样的事情也发生在:
Connection connection = pool.getConnection()
【讨论】:
那会有什么不同? @fcasanova 调试时更容易判断哪一行在哪个值之后抛出了哪个异常或错误。这不是一个答案,而是一个寻找它为什么不起作用的建议以上是关于即使明确设置 PreparedStatement 也不会超时的主要内容,如果未能解决你的问题,请参考以下文章
即使 PreparedStatement 仅使用一次,ResultSet 关闭错误后也不允许操作
为啥即使没有争用,我的 Oracle PreparedStatement 有时也不会返回?