6.1.3 数据库连接超时时间与终止
在使用数据库连接时会遇到的一个问题是,网络连接出现问题造成数据库连接中断。当数据库连接中断时,可能会造成发出数据库操作命令的线程在较长时间内处于等待状态。具体的等待时间取决于TCP连接的超时时间设置。这个超时时间一般会长达几分钟。也就是说,在数据库操作命令发出之后,可能需要等待几分钟才能得知数据库连接已经中断了。对于一个数据库应用来说,几分钟的等待时间过长,为此,JDBC 4.1添加了对数据库连接超时的处理方式,主要是在Connection接口中新增了setNetworkTimeout和abort两个方法。
Connection接口的setNetworkTimeout方法用来设置通过此数据库连接进行数据库操作时的超时等待时间。如果远程数据库没有在给定的时间内返回操作结果,那么会认为该连接已经关闭,无法再继续使用,处于等待状态的数据库操作方法则会抛出SQLException异常来表示这种超时的情况。对一个Connection接口的实现对象设置超时等待时间之后,会影响到从该Connection接口的实现对象中创建的其他对象,主要包括Statement和java.sql.PreparedStatement接口的实现对象。通过Statement和PreparedStatement接口的实现对象执行的查询操作也适用于Connection接口的实现对象上设置的超时等待时间。可以多次调用setNetworkTimeout方法,以对不同的情况设置不同的超时时间。对于同一个Connection接口的实现对象,如果预计某些查询操作比较耗时,可以把超时等待时间设置为一个较大的值,等操作完成之后,再恢复为一个对大多数操作都适用的较小值。
与setNetworkTimeout相关的方法abort用来强制关闭一个数据库连接。当调用一个Connection接口的实现对象的abort方法时,这个数据库连接会被标记为关闭状态,同时与该连接相关的资源都会被释放,另外,当前正在使用该连接的方法会抛出SQLException异常。从作用上讲,abort方法类似于Connection接口中已有的close方法,但是两者的使用场景不同:close方法一般是由Connection接口的使用者来调用的,当一个使用者完成数据库操作之后,可以通过close方法来关闭此数据库连接;而abort方法一般由数据库连接的管理者来调用,如果发生了前面提到的由于网络问题造成数据库连接中断的情况,连接的管理者在检测到问题发生之后,可以调用abort方法来强制终止此连接,该连接的使用者也不需要等待数据库操作的完成即可进入到SQLException异常的处理阶段。
setNetworkTimeout和abort方法都接收一个java.util.concurrent.Executor接口的实现对象作为参数。这个Executor接口的实现对象用来执行方法调用中可能会出现的相关任务。对于abort方法来说,在终止数据库连接的过程中需要释放相关的资源。释放资源的相关任务由该Executor接口的实现对象来执行。当abort方法返回之后,Executor接口的实现对象可能仍在继续执行相关的任务。代码清单6-3给出了abort方法的使用示例。为了查看在abort方法调用过程中执行的相关任务,这里使用了一个自定义的java. util.concurrent.ThreadPoolExecutor类的实现。在任务被执行之前,会在控制台输出相关的提示信息。在运行过程中可以发现,在调用abort方法之后,有新的任务被添加到Executor接口的实现对象中执行。
代码清单6-3 abort方法的使用示例
public class AbortConnection{
public void abortConnection()throws SQLException{
Connection connection=DriverManager.getConnection("jdbc:derby://localhost/java7book");
ThreadPoolExecutor executor=new DebugExecutorService(2,10,60,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
connection.abort(executor);
executor.shutdown();
try{
executor.awaitTermination(5,TimeUnit.MINUTES);
}catch(InterruptedException e){
e.printStackTrace();
}
}
private static class DebugExecutorService extends ThreadPoolExecutor{
public DebugExecutorService(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable>workQueue){
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public void beforeExecute(Thread t, Runnable r){
System.out.println("清理任务:"+r.getClass());
super.beforeExecute(t, r);
}
}
}