Wie kann ich kündigen eine Abfrage mit langer Lauf mit Spring und JDBCTemplate?
-
20-08-2019 - |
Frage
Die JDBC java.sql.Statement
Klasse hat eine cancel()
Methode. Dies kann in einem anderen Thread aufgerufen werden, um eine aktuell laufende Anweisung abzubrechen.
Wie kann ich dies mit Spring erreichen? Ich kann nicht einen Weg finden, einen Verweis auf eine Erklärung zu erhalten, wenn eine Abfrage ausgeführt wird. Ich kann auch nicht ein Abbrechen-ähnliches Verfahren finden.
Hier ist ein Beispielcode. Stellen Sie sich vor das dauert bis zu 10 Sekunden auszuführen, und manchmal auf die Anforderung des Benutzers, ich will es abbrechen:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
Wie würde ich ändere dies, so habe ich einen Verweis auf ein Objekt java.sql.Statement
?
Lösung
Lassen Sie mich oxbow_lakes Antwort vereinfachen. Sie PreparedStatementCreator
Variante der Abfrage-Methode verwenden, können den Zugriff auf die Erklärung zu gewinnen
So Ihr Code:
final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game");
Sollte verwandeln sich in:
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);
}
});
Jetzt abbrechen Sie gerade anrufen
stmt[0].cancel()
Sie wollen wahrscheinlich ein Hinweis auf stmt
zu einem anderen Thread geben, bevor die Abfrage tatsächlich ausgeführt wird, oder einfach speichern Sie es als eine Membervariable. Sie können aber nicht wirklich etwas aufheben ...
Andere Tipps
Sie können Sachen über JdbcTemplate
Methoden ausführen, die Sie erlauben in einem PreparedStatementCreator
zu passieren. Man könnte dies immer mit Anrufungen abfangen (vielleicht ein Proxy
verwenden), die eine cancel
verursacht auf einem separaten Thread geschehen durch einige cond
true
wurde.
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() { ... });
}
Dieses canceller
konnte sich nach dem erfolgreichen Abschluss abgebrochen werden; zum Beispiel
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);
So, jetzt der Code, der einen Benutzers Löschung reagiert würde wie folgt aussehen:
cond.set(true);
synchronized (cond) { cond.notifyAll(); }
Ich gehe davon aus von Spring Sie die Verwendung von JdbcDaoTemplate bedeuten und / oder JdbcTemplate? Wenn ja, ist dies nicht wirklich helfen oder behindern Sie Ihr Problem zu lösen.
Ich werde Ihr Anwendungsfall davon ausgehen, dass Sie einen DAO Betriebes in einem Thread sind die Ausführung und ein anderer Thread kommt und wollen den ersten Thread Vorgang abzubrechen.
Das erste Problem, das Sie lösen müssen, ist, wie funktioniert der zweite Thread wissen, was man zu kündigen? Ist das ein GUI mit einer festen Anzahl von Threads oder einem Server mit mehreren?
Wenn Sie diesen Teil gelöst haben, müssen Sie herausfinden, wie die Aussage in dem ersten Thread abzubrechen. Ein einfacher Ansatz dazu wäre der erste Thread PreparedStatement in einem Feld irgendwo zu speichern (vielleicht in einem einfachen Feld, vielleicht in einer Karte von Thread-ID-Anweisungen), so dass der zweite Thread kommen, rufen Sie den statwmentand Anruf abbrechen () auf sich.
Beachten Sie, dass es möglich ist, die cancel () kann nur blockieren, abhängig von Ihren JDBC-Treiber und Datenbank. Also, stellen Sie sicher, dass Sie über die Synchronisierung hart denken hier sind Ihre Fäden in einen Kampf bekommen gehen.
Sie können einen Rückruf Objekt vom Typ StatementCallback
auf JdbcTemplate
registrieren, die mit der aktuell aktiven Anweisung als Parameter ausgeführt werden erhalten. In diesem Rückruf können Sie dann brechen Sie die Anweisung:
simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() {
@Override
public Object doInStatement(final Statement statement) throws SQLException, DataAccessException {
if (!statement.isClosed()) {
statement.cancel();
}
return null;
}
});