Java 如何使用Spring和JDBCTemplate取消长时间运行的查询?
JDBCJava 如何使用Spring和JDBCTemplate取消长时间运行的查询?,java,spring,jdbctemplate,Java,Spring,Jdbctemplate,JDBCjava.sql.Statement类有一个cancel()方法。这可以在另一个线程中调用,以取消当前正在运行的语句 如何使用Spring实现这一点?在运行查询时,我找不到获取语句引用的方法。我也找不到类似取消的方法 下面是一些示例代码。想象一下,执行此操作需要10秒,有时在用户请求时,我想取消它: final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game"); 我该如何修改它,以便引用
java.sql.Statement
类有一个cancel()
方法。这可以在另一个线程中调用,以取消当前正在运行的语句
如何使用Spring实现这一点?在运行查询时,我找不到获取语句引用的方法。我也找不到类似取消的方法
下面是一些示例代码。想象一下,执行此操作需要10秒,有时在用户请求时,我想取消它:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
我该如何修改它,以便引用
java.sql.Statement
对象?您可以通过JdbcTemplate
方法执行内容,这些方法允许您传入PreparedStatementCreator
。您总是可以使用它来截获调用(可能使用代理
),该调用导致取消
在某个条件下在一个单独的线程上发生
public Results respondToUseRequest(Request req) {
final AtomicBoolean cond = new AtomicBoolean(false);
requestRegister.put(req, cond);
return jdbcTemplate.query(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection conn) {
PreparedStatement stmt = conn.prepareStatement();
return proxyPreparedStatement(stmt, cond);
}
},
new ResultSetExtractor() { ... });
}
此取消器
可在成功完成后自行取消;比如说
private final static ScheduledExecutorService scheduler =
Executors.newSingleThreadedScheduledExecutor();
PreparedStatement proxyPreparedStatement(final PreparedStatement s, AtomicBoolean cond) {
//InvocationHandler delegates invocations to the underlying statement
//but intercepts a query
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method m, Object[] args) {
if (m.getName().equals("executeQuery") {
Runnable cancel = new Runnable() {
public void run() {
try {
synchronized (cond) {
while (!cond.get()) cond.wait();
s.cancel();
}
} catch (InterruptedException e) { }
}
}
Future<?> f = scheduler.submit(cancel);
try {
return m.invoke(s, args);
} finally {
//cancel the canceller upon succesful completion
if (!f.isDone()) f.cancel(true); //will cause interrupt
}
}
else {
return m.invoke(s, args);
}
}
}
return (PreparedStatement) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[]{PreparedStatement.class},
h);
我假设Spring的意思是使用JdbcDaoTemplate和/或JdbcTemplate?如果是这样,这并不会真正帮助或阻碍你解决问题 我假设您的用例是在一个线程中执行DAO操作,而另一个线程进入并想要取消第一个线程的操作 您必须解决的第一个问题是,第二个线程如何知道取消哪个线程?这是一个具有固定线程数的GUI,还是一个具有多个线程数的服务器 一旦你解决了这个部分,你需要弄清楚如何取消第一个线程中的语句。一种简单的方法是将第一个线程的PreparedStatement存储在某个字段中(可能在一个简单的字段中,也可能在线程ID到语句的映射中),允许第二个线程进入,检索StatWindow并对其调用cancel()
请记住,cancel()可能只是阻塞,这取决于您的JDBC驱动程序和数据库。另外,请确保您认真考虑这里的同步问题,您的线程是否会陷入争斗。让我简化oxbow\u lakes的回答:您可以使用查询方法的
PreparedStatementCreator
变体来访问该语句
因此,您的代码:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
应变成:
final PreparedStatement[] stmt = new PreparedStatement[1];
final int i = (Integer)getJdbcTemplate().query(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
stmt[0] = connection.prepareStatement("select max(gameid) from game");
return stmt[0];
}
}, new ResultSetExtractor() {
public Object extractData(ResultSet resultSet) throws SQLException, DataAccessException {
return resultSet.getString(1);
}
});
现在你可以打电话取消
stmt[0].cancel()
在实际运行查询之前,您可能希望向其他线程提供对
stmt
的引用,或者简单地将其存储为成员变量。否则,您无法真正取消任何操作…您可以在JdbcTemplate
上注册类型为StatementCallback
的回调对象,该对象将以当前活动语句作为参数执行。在此回调中,您可以取消该语句:
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});
这是一个有趣而奇怪的例子,不完全确定如何将其应用于手头的问题。显然,10秒自动取消需要用外部触发的东西来替换。为什么它必须由外部触发?OP没有提到任何关于它的内容,比如说,这里是用户定义的OP:实际上,我确实希望在响应用户操作时触发取消。然后,我的方法也很简单。您可以运行某种侦听器,在完成时唤醒取消器,而不是一个只会休眠的取消器操作。我修改了我的回答我所做的修改只是概念的证明;很明显,你不想让你的线程池线程在现实中等待——你应该添加一些UserTriggerListener,这是非常好的详细信息,但我想回答的问题是:当通过JdbcTemplate运行查询时,我如何获得对语句的引用?你应该这么说:)正如@oxbow所说,您可以使用PreparedStatementCreator的自定义实例来控制语句的创建,然后将其传递给JdbcTemplate。
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});