我怎样才能取消使用Spring和JdbcTemplate的长时间运行的查询?
-
20-08-2019 - |
题
在JDBC java.sql.Statement
类有一个cancel()
方法。这可以在另一个线程调用取消当前正在运行的语句。
我怎样才能做到这一点使用Spring?我不能找到一种方法运行查询时得到一个语句引用。我也不能找到一个取消状方法。
下面是一些示例代码。想象这需要长达10秒来执行,并且有时在用户的请求时,我想取消:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
如何将修改这个所以我要一个java.sql.Statement
对象的引用?
解决方案
让我简化oxbow_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
方法,让你可以在PreparedStatementCreator
通过执行的东西。你总是可以用它来拦截这引起了Proxy
到一个单独的线程发生一些cancel
成为cond
调用(也许使用一个true
)。
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() { ... });
}
此canceller
本身能成功完成时被取消;例如
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);
所以,现在正在响应于用户的解除的代码将如下所示:
cond.set(true);
synchronized (cond) { cond.notifyAll(); }
我的春天假设你的意思是使用JdbcDaoTemplate和/或JdbcTemplate的吗?如果是这样,这并不能真正帮助或阻碍您解决问题。
我会假设你的使用情况是,你执行一个DAO操作在一个线程,而另一个线程进来,要取消第一个线程的操作。
您要解决的是,如何在第二个线程知道要取消哪一个的第一个问题?这是一个固定数量的线程,或几个服务器的GUI?
一旦你已经解决了一部分,你需要弄清楚如何取消声明中的第一个线程。一个简单的方法,这将是存储第一线程的PreparedStatement在外地的地方(也许在一个简单的领域,或许在地图线程ID到报表),允许第二线程进来,检索statwmentand调用取消()在上面。
请记住,这是可能的,取消()可能只是阻止,这取决于您的JDBC驱动程序和数据库。此外,请确保你好好想想同步这里,是你的线程将要进入战斗。
您可以注册在StatementCallback
类型JdbcTemplate
,将获得与当前活动语句作为参数执行的回调对象。在此回调可以再取消语句:
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});