2009-06-28 8 views
11

JDBC java.sql.Statementクラスのメソッドはcancel()です。これは、現在実行中の文を取り消すために別のスレッドで呼び出すことができます。SpringとJDBCTemplateを使用して長期実行クエリを取り消すにはどうすればよいですか?

Springを使用してこれを実現するにはどうすればよいですか?私は、クエリを実行しているときにステートメントへの参照を取得する方法を見つけることができません。また、キャンセルのような方法を見つけることもできません。

ここにいくつかのサンプルコードがあります。これを実行するために10秒を要し、時には、ユーザの要求に応じて、私はそれをキャンセルしたいと想像:

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

どのように私はこれを修正しますので、私はjava.sql.Statementオブジェクトへの参照を持っていますか?

答えて

11

私はoxbow_lakesの答えを簡潔にしましょう:クエリメソッドのPreparedStatementCreatorバリアントを使用して、ステートメントにアクセスできます。

だからあなたのコード:あなたはちょうどあなたがおそらくにstmtへの参照を与えたい

stmt[0].cancel() 

を呼び出すことができますキャンセルする

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

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

はに変わります実際にクエリを実行する前の他のスレッド、または単にメンバ変数として格納するe。そうでなければ、あなたは本当にあなたがパラメータとして、現在アクティブなステートメントを使用して実行されますJdbcTemplateにタイプStatementCallbackのコールバックオブジェクトを登録することができます

1

PreparedStatementCreatorで渡すことができるJdbcTemplateメソッドを介してものを実行することができます。これを使用して(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(); } 
+0

面白いと奇数たとえば、その手で問題に適用する方法を全くわからない:このコールバックでは、その文をキャンセルすることができます。明らかに、10秒の自動キャンセルは、外部からトリガされたものに置き換える必要があります。 – skaffman

+0

なぜ外部からトリガーする必要がありますか? OPはそれについて、何も言及していません、つまり、ユーザー定義の –

+0

OPはここにあります:実際には、私はキャンセルがユーザーの操作に応じてトリガーされることを望みます。 –

0

私はあなたがJdbcDaoTemplateおよび/またはJdbcTemplateの使用を意味する春のことで仮定しますか?もしそうなら、これは本当にあなたの問題を解決するのにあなたを助けたり妨げたりしません。

私はあなたが1つのスレッドでDAO操作を実行しており、別のスレッドが入ってきて、最初のスレッドの操作をキャンセルしたいと仮定します。

解決しなければならない最初の問題は、2番目のスレッドがどのスレッドを取り消すかを知ることです。これは固定数のスレッドを持つGUIですか、いくつかのサーバーを持つGUIですか?

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

JDBCドライバとデータベースによってcancel()がブロックされる可能性があることに注意してください。また、ここで同期について懸命に考えることを確認してください、あなたのスレッドは戦いに入るだろうか。

+0

これはすばらしい詳細情報ですが、 JdbcTemplateを介してクエリを実行しているときに、どのようにステートメントへの参照を取得することができますか? –

+0

さて、そう言ったはずです:) @oxbowによると、PreparedStatementCreatorのカスタムインスタンスを使用して文の作成を制御し、それをJdbcTemplateに渡すことができます。 – skaffman

0

...何かをキャンセルすることはできません。

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

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

     return null; 
    } 
}); 
関連する問題