Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用Spring和JDBCTemplate取消长时间运行的查询?_Java_Spring_Jdbctemplate - Fatal编程技术网

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

Java 如何使用Spring和JDBCTemplate取消长时间运行的查询?,java,spring,jdbctemplate,Java,Spring,Jdbctemplate,JDBCjava.sql.Statement类有一个cancel()方法。这可以在另一个线程中调用,以取消当前正在运行的语句 如何使用Spring实现这一点?在运行查询时,我找不到获取语句引用的方法。我也找不到类似取消的方法 下面是一些示例代码。想象一下,执行此操作需要10秒,有时在用户请求时,我想取消它: final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game"); 我该如何修改它,以便引用

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

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

下面是一些示例代码。想象一下,执行此操作需要10秒,有时在用户请求时,我想取消它:

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

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

您可以通过
JdbcTemplate
方法执行内容,这些方法允许您传入
PreparedStatementCreator
。您总是可以使用它来截获调用(可能使用
代理
),该调用导致
取消
在某个
条件下在一个单独的线程上发生

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() { ... });
}        
取消器
可在成功完成后自行取消;比如说

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

我假设Spring的意思是使用JdbcDaoTemplate和/或JdbcTemplate?如果是这样,这并不会真正帮助或阻碍你解决问题

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

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

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


请记住,cancel()可能只是阻塞,这取决于您的JDBC驱动程序和数据库。另外,请确保您认真考虑这里的同步问题,您的线程是否会陷入争斗。

让我简化oxbow\u 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
上注册类型为
StatementCallback
的回调对象,该对象将以当前活动语句作为参数执行。在此回调中,您可以取消该语句:

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

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

        return null;
    }
});

这是一个有趣而奇怪的例子,不完全确定如何将其应用于手头的问题。显然,10秒自动取消需要用外部触发的东西来替换。为什么它必须由外部触发?OP没有提到任何关于它的内容,比如说,这里是用户定义的OP:实际上,我确实希望在响应用户操作时触发取消。然后,我的方法也很简单。您可以运行某种侦听器,在完成时唤醒取消器,而不是一个只会休眠的取消器操作。我修改了我的回答我所做的修改只是概念的证明;很明显,你不想让你的线程池线程在现实中等待——你应该添加一些UserTriggerListener,这是非常好的详细信息,但我想回答的问题是:当通过JdbcTemplate运行查询时,我如何获得对语句的引用?你应该这么说:)正如@oxbow所说,您可以使用PreparedStatementCreator的自定义实例来控制语句的创建,然后将其传递给JdbcTemplate。
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {

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

        return null;
    }
});