JDBC java.sql.Statement
类具有cancel()
方法。这可以在另一个线程中调用,以取消当前正在运行的语句。
如何使用Spring实现这一目标?我无法找到在运行查询时获取对语句的引用的方法。我也找不到类似取消的方法。
这是一些示例代码。想象一下,这需要10秒才能执行,有时根据用户的要求,我想取消它:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
我如何修改它,以便我引用java.sql.Statement
对象?
答案 0 :(得分:12)
让我简化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
,或者只是将其存储为成员变量。否则,你真的无法取消任何东西......
答案 1 :(得分:1)
您可以通过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(); }
所以现在响应用户取消的代码如下所示:
{{1}}
答案 2 :(得分:0)
我认为Spring是指使用JdbcDaoTemplate和/或JdbcTemplate?如果是这样,这并没有真正帮助或妨碍您解决问题。
我假设你的用例是你在一个线程中执行DAO操作,另一个线程进来并想要取消第一个线程的操作。
你必须解决的第一个问题是,第二个线程如何知道取消哪一个?这是一个具有固定线程数的GUI,还是具有多个线程的服务器?
一旦你解决了那个部分,你需要弄清楚如何取消第一个线程中的语句。一个简单的方法是将第一个线程的PreparedStatement存储在某个字段中(可能在一个简单的字段中,可能在一个线程ID到语句的映射中),允许第二个线程进入,检索statwment并调用cancel()在上面。
请记住,cancel()可能会阻塞,具体取决于您的JDBC驱动程序和数据库。另外,请确保你在这里认真考虑同步,你的线程是否会陷入战斗。
答案 3 :(得分:0)
您可以在StatementCallback
上注册JdbcTemplate
类型的回调对象,该回调对象将以当前活动语句作为参数执行。在此回调中,您可以取消声明:
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});