どのように私は春とJDBCTemplateを使用して実行時間の長いクエリをキャンセルすることができますか?
-
20-08-2019 - |
質問
JDBCのjava.sql.Statement
クラスはcancel()
メソッドを持っています。これは、現在実行中の文をキャンセルし、別のスレッドで呼び出すことができます。
どのように私は春を使用して、これを達成することができますか?私は、クエリを実行するときのステートメントへの参照を取得する方法を見つけることができません。また私は、キャンセルのような方法を見つけることができます。
ここではいくつかのサンプルコードです。これは、ユーザーの要求に応じて実行するために10秒を要し、時には想像し、私はそれをキャンセルしたい:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
私は、私はjava.sql.Statement
オブジェクトへの参照を持って、これを修正するだろうか?
解決
私は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
、あるいは単にメンバ変数としてそれを格納するための基準を与えたいです。そうしないと、あなたが本当に何を取り消すことができません...
他のヒント
あなたはJdbcTemplate
に通過できるようPreparedStatementCreator
メソッドを使ってものを実行することができます。あなたは、常にいくつかの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(); }
私はあなたがJdbcDaoTemplateおよび/またはJdbcTemplateの使用を意味する春のことで仮定しますか?もしそうなら、これは本当にあなたの問題を解決のお手伝いをしたり妨げません。
私はあなたのユースケースは、あなたが1つのスレッドでDAO操作を実行していることであり、別のスレッドがで来て、最初のスレッドの操作をキャンセルしたいと仮定します。
あなたが解決しなければならない最初の問題は、キャンセルするかを第二のスレッドを知っているか、でしょうか?これは、スレッドの固定数、またはいくつかを使用してサーバーとのGUIですか?
あなたはその部分を解決したら、は、最初のスレッドで文をキャンセルする方法を把握する必要があります。これに対する1つの単純なアプローチは、()、第二のスレッドがでてくることができ、(おそらく文にスレッドIDのマップでは、おそらく簡単なフィールドで)どこかのフィールドに最初のスレッドのPreparedStatementを保存statwmentandコールがキャンセル取得することですその上に。
心の中でベアそれは(キャンセルいる可能性がありますことを)ちょうどあなたのJDBCドライバとデータベースに依存し、ブロックすることがあります。また、あなたはあなたのスレッドが戦いに得ようとしているが、ここでは同期についてハードだと思うことを確認します。
あなたは、パラメータとして、現在アクティブなステートメントを使用して実行されますStatementCallback
上型JdbcTemplate
のコールバックオブジェクトを登録することができます。このコールバックでは、その文をキャンセルすることができます:
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});