Spring 및 JDBCTemplate을 사용하여 장기 실행 쿼리를 어떻게 취소 할 수 있습니까?

StackOverflow https://stackoverflow.com/questions/1054642

  •  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;
    }
});
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top