使用 JDBC 取消 SQL 语句
Posted
技术标签:
【中文标题】使用 JDBC 取消 SQL 语句【英文标题】:Cancel SQL Statement with JDBC 【发布时间】:2015-02-12 15:49:44 【问题描述】:我在这里遇到了这个问题。我正在我的 Tomcat 应用程序服务器上运行一个应用程序。作为前端,我使用的是一个带有 javascript 的 html 网站,在后端我使用的是 Java。
当用户单击一个按钮时,会进行多个 sql 查询,一个接一个。现在,如果用户愿意,我想提供取消此查询的功能。
我已经检查了我的 jdbc 驱动程序和数据库是否与cancel()
方法兼容,这很好。
这是我的代码:
PreparedStatement stmt = null;
public void runQuery(String query)
Connection con = getConnection();
try
stmt = con.prepareStatement(query);
stmt.execute();
catch(SQLException e)
e.printStackTrace();
finally
if(stmt != null && !stmt.isClosed())
stmt.close();
if(con != null)
con.close();
public void cancelQuery()
try
if(stmt != null && !stmt.isClosed())
stmt.cancel();
catch (SQLException e)
e.printStackTrace();
所以用户点击运行按钮 => runQuery
被执行,stmt 被初始化/覆盖为需要执行的查询。
然后用户点击取消按钮 => cancelQuery
被执行。
不幸的是,我有时会收到 NullPointerException,因为 stmt 为空。但如果 stmt 为 null ,它甚至不应该调用 cancelQuery ?!
这是堆栈跟踪:
Stacktrace:] with root cause
java.lang.NullPointerException
at com.sybase.jdbc3.jdbc.SybStatement.doCancel(SybStatement.java:646)
at com.sybase.jdbc3.jdbc.SybStatement.cancel(SybStatement.java:614)
at org.apache.tomcat.dbcp.dbcp2.DelegatingStatement.cancel(DelegatingStatement.java:269)
at org.apache.tomcat.dbcp.dbcp2.DelegatingStatement.cancel(DelegatingStatement.java:269)
at de.package.util.DBHelper.cancelQuery(DBHelper.java:82)
.....
知道为什么这会不断产生异常吗?我怎样才能以正确的方式取消声明?
编辑: 我已经查看了 cmets 中的链接,现在从不同的线程运行 cancel() 方法。然而 NullPointer 仍然发生。这就是我现在调用 cancel() 方法的方式:
public void cancelQuery()
Thread thread = new Thread(new SQLCancelRunnable(stmt));
thread.start();
public class SQLCancelRunnable implements Runnable
PreparedStatement stmt;
public SQLCancelRunnable(PreparedStatement stmt)
this.stmt = stmt;
@Override
public void run()
if(stmt != null)
try
System.out.println(stmt);
System.out.println(stmt.toString());
stmt.cancel();
System.out.println("canceled");
catch (SQLException e)
e.printStackTrace();
EDIT2 找到我的答案,问题是 runQuery() 方法的 finally 块。因为我关闭了语句和连接,所以抛出了 NullPointer。 我现在删除了这个块,但这当然会导致大量资源泄漏。谁能指导我正确地关闭我的资源?
【问题讨论】:
见***.com/q/16589497/217324 @NathanHughes 请看看我编辑的问题。该链接没有带来整体解决方案:( 我会说,为什么要担心呢?只需捕获 NullPointerException。 既然 NullPointerException 是一个运行时异常,我不应该抓住它而是避免它对吗? @dehlen ,查看更新答案了解如何释放资源 【参考方案1】: PreparedStatement stmt = null;
public void runQuery(String query)
Connection con = getConnection();
try
stmt = con.prepareStatement(query);
stmt.execute();
catch(SQLException e)
e.printStackTrace();
finally
if(stmt != null && !stmt.isClosed())
stmt.close();
if(con != null)
con.close();
public void cancelQuery()
try
if(stmt != null && !stmt.isClosed())
stmt.cancel();
catch (SQLException e)
e.printStackTrace();
catch (Exception e)
e.printStackTrace();
试试这个。我在 SQLException 之后添加了一个通用异常。
不能说这是一个非常干净的解决方案,但它会忽略可能由 stmt.close() 语句引发的空指针异常。
【讨论】:
既然这是我现在采用的答案,我会将您标记为已接受,尽管它可能不是最干净的解决方案..【参考方案2】:你可以使用Statement.cancel()
正如 Java 文档所说
void cancel()
throws SQLException
如果 DBMS 和驱动程序都支持,则取消此 Statement 对象 中止 SQL 语句。该方法可由一个线程使用 取消另一个线程正在执行的语句。
如果查询执行超过阈值时间,您也可以设置setQueryTimeout
java.sql.Statement.setQueryTimeout(seconds)
更新
别忘了Rollback交易
任何可以指导我正确方向的人如何关闭我的 资源正确吗?
这就是 finally 块的发明
finally
//Release All Resources
finally 块总是在 try 块退出时执行。这 确保即使出现意外情况也会执行 finally 块 发生异常。但 finally 不仅仅对异常有用 处理——它允许程序员避免清理代码 意外地被返回、继续或中断绕过。进行清理 finally 块中的代码始终是一个好习惯,即使没有 预计会有例外情况。
【讨论】:
感谢您的回答,但基本上您描述的是我已经尝试过的内容。我关闭了 finally 块中的所有资源,并从不同的线程运行 cancel() 方法。【参考方案3】:您需要同步语句关闭:
public void runQuery(String query)
...
try
stmt = con.prepareStatement(query);
stmt.execute();
...
finally
synchronized(this)
if(stmt != null && !stmt.isClosed())
stmt.close();
public void cancelQuery()
synchronized(this)
if(stmt != null && !stmt.isClosed())
stmt.cancel();
在每个语句之间,另一个线程可能会执行某种代码,因此一个简单的 if 不足以确保世界的状态与您期望的一样。
在this
上同步可能不是最佳选择,但由于stmt
可能为空,我们不能使用此对象。
编辑:如果启动查询的代码是异步的,您还必须准备好调用cancelQuery
,甚至在您的语句准备好之前。
【讨论】:
【参考方案4】:你应该看看Apache DB-Utils,它让这种问题消失了,你可以简单地写这样的东西:
finally
DbUtils.closeQuietly(resutlSet);
DbUtils.closeQuietly(preparedStatement);
DbUtils.closeQuietly(connection);
【讨论】:
2021年这个还常用吗?还是同时开发了一些更现代的东西? 从今天开始,应该将 try 与资源块一起使用,这可能仍然会抛出 SqlExceptions,但它们至少可以正确处理 try 或 finally 中引发的 NPE 和异常(记住永远不要在 finally 中抛出异常块?)以上是关于使用 JDBC 取消 SQL 语句的主要内容,如果未能解决你的问题,请参考以下文章