即使显式设置,PreparedStatement 也永远不会超时
PreparedStatement won't ever timeout even if explicitly set
我正在尝试模拟我的服务失去与数据库的连接并且无法通过阻止与 iptables 的连接来执行 INSERT
的场景,但我无法使用 executeQuery()
方法来超时。
我所做的就是像这样为 PreparedStatement 设置超时 statement.setQueryTimeout(5)
。这是代码。
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
然后它永远挂起...我该怎么做才能让它抛出异常?
你总是可以:
try{
preparedstatement = connection.prepareStatement(query);
preparedstatement.setQueryTimeout(seconds);
}
而不是你所做的。也许这样会更好。
同样的事情也适用于:
Connection connection = pool.getConnection()
最后调用 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 流量,如果未设置网络超时,它可能会挂起连接。
我正在尝试模拟我的服务失去与数据库的连接并且无法通过阻止与 iptables 的连接来执行 INSERT
的场景,但我无法使用 executeQuery()
方法来超时。
我所做的就是像这样为 PreparedStatement 设置超时 statement.setQueryTimeout(5)
。这是代码。
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
然后它永远挂起...我该怎么做才能让它抛出异常?
你总是可以:
try{
preparedstatement = connection.prepareStatement(query);
preparedstatement.setQueryTimeout(seconds);
}
而不是你所做的。也许这样会更好。 同样的事情也适用于:
Connection connection = pool.getConnection()
最后调用 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 流量,如果未设置网络超时,它可能会挂起连接。