17

JDBCjava.sql.Statement类有一个cancel()方法。这可以在另一个线程中调用以取消当前正在运行的语句。

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

这是一些示例代码。想象一下,这需要 10 秒才能执行,有时根据用户的请求,我想取消它:

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

我将如何修改它以便引用一个java.sql.Statement对象?

4

4 回答 4

14

让我简化 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在实际运行查询之前引用其他线程,或者只是将其存储为成员变量。否则,你真的不能取消任何东西......

于 2009-07-29T15:11:57.330 回答
2

您可以通过JdbcTemplate允许您传入PreparedStatementCreator. 您始终可以使用它来拦截调用(可能使用 a Proxy),这些调用导致 acancel在单独的线程上由 some condbecome发生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(); }
于 2009-06-28T12:17:49.663 回答
1

您可以注册一个类型为StatementCallbackon的回调对象,JdbcTemplate该对象将使用当前活动的语句作为参数执行。在此回调中,您可以取消该语句:

simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {

    @Override
    public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
        if (!statement.isClosed()) {
            statement.cancel();
        }

        return null;
    }
});
于 2009-11-26T10:40:50.060 回答
0

我假设 Spring 是指使用 JdbcDaoTemplate 和/或 JdbcTemplate?如果是这样,这并不能真正帮助或阻碍您解决问题。

我假设您的用例是您正在一个线程中执行 DAO 操作,而另一个线程进入并想要取消第一个线程的操作。

您要解决的第一个问题是,第二个线程如何知道要取消哪个?这是具有固定线程数的 GUI,还是具有多个线程的服务器?

一旦你解决了那部分,你需要弄清楚如何取消第一个线程中的语句。一种简单的方法是将第一个线程的 PreparedStatement 存储在某个字段中(可能在一个简单字段中,可能在线程 ID 到语句的映射中),允许第二个线程进入,检索状态并调用 cancel()在上面。

请记住,cancel() 可能只是阻塞,具体取决于您的 JDBC 驱动程序和数据库。另外,请确保您在这里认真考虑同步,您的线程是否会发生冲突。

于 2009-06-29T07:24:46.370 回答