Spring 및 JDBCTemplate을 사용하여 장기 실행 쿼리를 어떻게 취소 할 수 있습니까?
-
20-08-2019 - |
문제
JDBC java.sql.Statement
클래스는 a 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
) a 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의 사용을 의미한다고 가정합니까? 그렇다면 문제를 해결하는 데 실제로 도움이되지 않거나 방해하지 않습니다.
유스 케이스는 하나의 스레드에서 DAO 작업을 실행하고 다른 스레드가 들어 와서 첫 번째 스레드 작업을 취소하고 싶다고 가정합니다.
해결해야 할 첫 번째 문제는 두 번째 스레드가 어떤 스레드가 취소 해야하는지 어떻게 알 수 있습니까? 고정 된 수의 스레드가있는 GUI입니까, 아니면 여러 개의 서버입니까?
해당 부분을 해결하면 첫 번째 스레드에서 문을 취소하는 방법을 알아 내야합니다. 이에 대한 간단한 접근 방식 중 하나는 첫 번째 스레드의 준비된 상태를 어딘가에 필드에 저장하는 것입니다 (아마도 간단한 필드, 아마도 스레드 ID의 맵에서 문의 맵에서) 두 번째 스레드가 들어올 수있게하여 statwmentand call cancel ()를 검색하는 것입니다. 그 위에.
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;
}
});