【问题标题】:How to force timeout for DriverManager.getConnection() method call?如何强制 DriverManager.getConnection() 方法调用超时?
【发布时间】:2011-06-18 13:38:09
【问题描述】:

我有一个应用程序,它将与 MySQL 建立数据库连接并执行查询。有时DriverManager.getConnection() 方法调用需要 2 秒,有时需要 30 秒。有没有办法控制这个方法在2秒后超时?

DriverManager.setLoginTimeout() 似乎不起作用。

实际上,我可以为statement.executeQuery() 设置超时,方法是让线程休眠以获得我的超时值并在唤醒后关闭连接。但它是我无法真正设置超时的连接建立部分。

不胜感激。

【问题讨论】:

  • 您正在使用哪个驱动程序以及用于哪个数据库?

标签: java timeout database-connection


【解决方案1】:

如果没有其他选项,您总是可以在单独的线程中执行调用,如果它没有在 2 秒内完成,您可以中止/忽略它。

编辑 这是我的想法的一个例子:

public class Dummy extends Thread {
private volatile Connection conn = null;
@Override
public void run() {
    try {
        this.conn = DriverManager.getConnection("foobar") ;
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
static public Connection getConnection() {
    Dummy d = new Dummy() ;
    d.start() ;
    try {
        Thread.sleep(2000) ;
    } catch (InterruptedException e) {}
    return d.conn ;
}
}

然后您可以在代码的其他位置调用静态 Dummy.getConnection() 方法。一个缺点是这种方法总是需要 2 秒,但是将其更改为在线程完成后立即返回并不难。

【讨论】:

  • 如何中止线程? 2秒后中断线程不起作用。
  • 好吧,如果你认为你必须中止线程,你总是可以在它上面调用 stop(),但这是一个不推荐使用的方法,所以首先阅读文档中的任何潜在缺陷。您也可以让它继续运行,而您忽略它并继续前进。
  • 对不起,但是 thread.stop() 说“如果目标线程等待很长时间(例如在条件变量上),应该使用中断方法来中断等待。”但在我的情况下,interrupt() 没有帮助。我可以知道原因吗?
【解决方案2】:

感谢 codebolt,我不知道这是否是最好的解决方案,但这对我有用。 10 秒超时。

public class Dummy extends Thread {
             private volatile java.sql.Connection conn = null;
             private boolean sleep = true;
            @Override
             public void run() {
                 try {

                     String driver = "net.sourceforge.jtds.jdbc.Driver";
                     Class.forName(driver).newInstance();                       
                     //timeout
                     DriverManager.setLoginTimeout(10);
                     this.conn = DriverManager.getConnection(url, user, pwd);
                     sleep = false;
                 } catch (Exception e) {}
             }
             public java.sql.Connection getConnection() {
                 Dummy d = new Dummy() ;
                 d.start() ;
                 try {
                     for(int i=1; i<=10; i++) {
                         //Wait 1 second
                         if (d.sleep){
                             Thread.sleep(1000);  
                         }
                     }  
                 } catch (InterruptedException e) {}
                 return d.conn ;
             }
             }

还有电话:

Dummy a = new Dummy();
connection = a.getConnection();
if (connection != null) {....

【讨论】:

  • anonymous user发布:If you make the getConnection() method static, you don't have to create object a which is never used. Going further, you could make the driver class name, URL, user name, and passwords arguments to getConnection() and have something quite general purpose.
【解决方案3】:

您可以使用 Java 中的ExecutorService 接口。以下是您需要做的一个示例。

Future<Boolean> future = executor.submit(YOUR_METHOD);
future.get(TIMEOUT_YOU_NEED, TimeUnit.SECONDS);

【讨论】:

    【解决方案4】:

    尝试在连接 URL 或连接池(如果您使用池化)上设置 socketTimeout(以毫秒为单位的时间)。注意不要将此值设置得太低,否则会覆盖语句超时值。

    try {
        this.conn = DriverManager.getConnection("url?socketTimeout=2000") ;
    } catch (SQLException e) {
        e.printStackTrace();
    }
    

    <jdbc-connection-pool 
                         connection-validation-method="table"
                         fail-all-connections="true"
                         idle-timeout-in-seconds="300"
                         is-connection-validation-required="true"
                         statement-timeout-in-seconds="2"
                         validation-table-name="dual"
                         ..... >
       <property name="databaseName" value="...."/>
       <property name="serverName" value="....."/>
       <property name="User" value="....."/>
       <property name="Password" value="......."/>
       <property name="URL" value="jdbc:mysql://...../...."/>
       <property name="driverClass" value="...."/>
       <property name="socketTimeout" value="2000"/>
    </jdbc-connection-pool>
    

    设置此项为我解决了超时问题。

    【讨论】:

      【解决方案5】:

      我扩展了 CodeBolt 和 anpadia 的答案,以制作以下完整的课程。它只有一种静态方法可以接受所有需要的连接参数,包括超时时间。此外,如果发生 SQLException 和 ClassNotFoundException,该方法也会抛出它们。

      用法:

      String connectionUrl = "...";
      String user = "...";
      String password = "...";
      String driver = "org.postgresql.Driver"; // for example
      
      int timeoutInSeconds = 5;
      
      Connection myConnection =
          ConnectWithTimeout.getConnection(connectionUrl, user, password, driver, timeoutInSeconds);
      

      下面是实现:

      import java.sql.DriverManager;
      import java.sql.Connection;
      import java.sql.SQLException;
      
      
      public class ConnectWithTimeout extends Thread {
      
          private static String _url;
          private static String _user;
          private static String _password;
          private static String _driver;
      
          private static volatile Connection _connection = null;
          private static volatile boolean _sleep = true;
          private static volatile SQLException _sqlException = null;
          private static volatile ClassNotFoundException _classNotFoundException = null;
      
          @Override
          public void run() {
              try {
                  Class.forName(_driver);
                  _connection = DriverManager.getConnection(_url, _user, _password);
              }
              catch (SQLException ex) {
                  _sqlException = ex;
              }
              catch (ClassNotFoundException ex) {
                  _classNotFoundException = ex;
              }
              _sleep = false;
          }
      
          public static Connection getConnection(String url, 
                                                 String user, 
                                                 String password, 
                                                 String driver, 
                                                 int timeoutInSeconds) 
              throws SQLException, ClassNotFoundException {
      
              checkStringOrThrow(url,      "url");
              checkStringOrThrow(user,     "user");
              checkStringOrThrow(password, "password");
              checkStringOrThrow(driver,   "driver");
      
              if (timeoutInSeconds < 1) {
                  throw new IllegalArgumentException(
                      "timeoutInSeconds must be positive");
              }
      
              _url = url;
              _user = user;
              _password = password;
              _driver = driver;
      
              ConnectWithTimeout conn = new ConnectWithTimeout();
              conn.start();
      
              try {
                  for (int i = 0; i < timeoutInSeconds; i++) {
                      if (_sleep) {
                          Thread.sleep(1000);
                      }
                  }
              }
              catch (InterruptedException ex) {
              }
      
              if (_sqlException != null) {
                  throw _sqlException;
              }
      
              if (_classNotFoundException != null) {
                  throw _classNotFoundException;
              }
      
              return _connection;
          }
      
          private static void checkStringOrThrow(String variable, String variableName) {
              if (variable == null || variable.length() == 0) {
                  throw new IllegalArgumentException(
                      "String is null or empty: " + variableName);
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        【讨论】:

        • 但我认为 setLoginTimeout() 实现不适用于 jdbc-mysql 驱动程序。
        • @Haroldis:那个链接已经烂掉了。请在答案中包含您的发现。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-09
        • 2011-09-02
        • 2017-03-14
        • 1970-01-01
        • 2015-05-13
        • 1970-01-01
        • 2010-11-19
        相关资源
        最近更新 更多