如何使用Spring和JDBCTemplate取消长时间运行的查询?

2022-09-03 02:13:17

JDBC 类有一个方法。可以在另一个线程中调用它以取消当前正在运行的语句。java.sql.Statementcancel()

如何使用Spring实现这一目标?在运行查询时,我找不到获取对语句的引用的方法。我也找不到类似取消的方法。

下面是一些示例代码。想象一下,这最多需要10秒来执行,有时在用户的请求下,我想取消它:

    final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");

如何修改它以便对对象的引用?java.sql.Statement


答案 1

让我简化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


答案 2

您可以通过允许您传入 .你总是可以使用它来拦截调用(也许使用a),这导致一些在单独的线程上发生一个变成了。JdbcTemplatePreparedStatementCreatorProxycancelcondtrue

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(); }

推荐