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