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。いつでもこれを使用して、別のスレッドでProxy発生する呼び出しを(おそらくを使用して)インターセプトすることができます。cancelcondtrue

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操作を実行していて、別のスレッドが入って最初のスレッドの操作をキャンセルしたいというユースケースを想定します。

解決しなければならない最初の問題は、2番目のスレッドがどちらをキャンセルするかをどのように知るかということです。これは固定数のスレッドを備えたGUIですか、それとも複数のスレッドを備えたサーバーですか?

その部分を解決したら、最初のスレッドでステートメントをキャンセルする方法を理解する必要があります。これに対する簡単なアプローチの1つは、最初のスレッドのPreparedStatementをどこかのフィールド(おそらく単純なフィールド、おそらくスレッドIDからステートメントへのマップ)に格納し、2番目のスレッドが入ってstatwmentを取得してcancel()を呼び出すことです。その上に。

JDBCドライバーとデータベースによっては、cancel()がブロックされる可能性があることに注意してください。また、ここで同期についてよく考えてください。スレッドが競合することになりますか。

于 2009-06-29T07:24:46.370 に答える