Как я могу отменить длительный запрос, используя Spring и JdbcTemplate?

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

  •  20-08-2019
  •  | 
  •  

Вопрос

В JDBC java.sql.Statement класс имеет cancel() способ.Это может быть вызвано в другом потоке, чтобы отменить выполняемую в данный момент инструкцию.

Как я могу добиться этого, используя Spring?Я не могу найти способ получить ссылку на оператор при выполнении запроса.Я также не могу найти метод, подобный отмене.

Вот несколько примеров кода.Представьте, что выполнение этого занимает до 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(); }

Я предполагаю, что под Spring вы подразумеваете использование JdbcDaoTemplate и / или JdbcTemplate?Если да, то это на самом деле не помогает и не мешает вам в решении вашей проблемы.

Я предположу, что ваш вариант использования заключается в том, что вы выполняете операцию DAO в одном потоке, а приходит другой поток и хочет отменить операцию первого потока.

Первая проблема, которую вам нужно решить, заключается в том, как второй поток узнает, какой из них отменить?Является ли это графическим интерфейсом с фиксированным количеством потоков или сервером с несколькими?

Как только вы решите эту часть, вам нужно выяснить, как отменить инструкцию в первом потоке.Одним из простых подходов к этому было бы сохранить PreparedStatement первого потока где-нибудь в поле (возможно, в простом поле, возможно, в сопоставлении идентификатора потока с инструкциями), позволяя второму потоку войти, извлечь statwmentи вызвать cancel() для него.

Имейте в виду, что возможно, что 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