Как отменить подготовленное заявление iBatis? - PullRequest
3 голосов
/ 03 марта 2011

Мне нужно решение для отмены долгосрочного оператора выбора. Я использую Spring 3.0.2, iBatis 2.3.0 и Oracle 10g. Мне удалось заставить его работать с простым JDBC, но поскольку выбор генерируется динамически через экран расширенного поиска, мне действительно нужно использовать iBatis.

Внутренний класс iBatis, отвечающий за создание / извлечение из кэша подготовленных операторов: com.ibatis.sqlmap.engine.execution.SqlExecutor . Внутренний метод, вызываемый для каждого вызова queryForList () / queryForObject () , является SqlExecutor's

public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException Метод.

По соображениям производительности iBatis создает новый подготовленный оператор, только если он еще не существует для данного оператора выбора. Подготовленные операторы хранятся / кэшируются в HashMap, где строка sql является ключом, а подготовленный оператор - значением.

После неудачных попыток найти другие решения, я думаю, что можно было бы поработать с AOP ( AspectJ ), чтобы попытаться навести точку на метод SqlExecutor.executeQuery () и как-то сохранить в сеансе HTTP Карта кэша iBatis и строка sql.

Когда пользователь попытается отменить длительный запрос, из другого потока будет сделана проверка, чтобы увидеть, существует ли уже подготовленный оператор в карте кэша iBatis для данной строки sql, ранее сохраненной в сеансе HTTP через АОП. Если он существует, будет выполнен вызов Statement.cancel (). Я не понимаю, почему подобное решение может мешать внутренним механизмам iBatis, поскольку, если подготовленный оператор будет отменен, будет выдано исключение SqlException ( ORA-01013 пользователь запросил отмену текущей операции ) и Ibatis будет правильно обрабатывать это, как и любой другой сгенерированный SqlException.

Использование Spring AOP не является опцией, поскольку позволяет только указывать методы, объявленные в объектах, управляемых контейнером Spring. Я не могу объявить SqlExecutor как bean-компонент Spring, потому что он создается и управляется внутри iBatis.

Еще не пробовали вышеуказанное решение с AspectJ, так как я не совсем знаком с фреймворком AspectJ.

Я не уверен, что это правильный подход для этого, но я не нашел другого решения, чтобы отменить подготовленный оператор, созданный iBatis, поскольку iBatis, кажется, не предлагает никакой поддержки для этого (также проверил myBatis).

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

Я знаю, вопрос старый, но, возможно, кто-то найдет это полезным.

Вы можете использовать механизм плагинов, предоставляемый iBatis.Вот рабочий пример, хотя и немного сложный.Он не требует какой-либо конкретной библиотеки или среды, только iBatis / myBatis (здесь используется версия 3.x).

Сначала мы определим интерфейс Cancelable:

public interface Cancelable {
    void cancel();
}

Затем у нас есть обработчик, который хранит последние java.sql.Statement и отменяет их по запросу:

class CancelHandler implements Cancelable {

    Statement lastStatement;

    @Override
    public void cancel() {
        if (lastStatement != null) {
            try {
                lastStatement.cancel();
            } catch (SQLException e) {
                // cancel failed or not supported (ignore)
            }
        }
    }
}

Чтобы запомнить последнее утверждение, нам нужно обернуть org.apache.ibatis.executor.statement.StatementHandler:

public class StatementHandlerWrapper implements StatementHandler {

    private final StatementHandler wrapped;

    private static final ThreadLocal<CancelHandler> CANCEL_HANDLER = ThreadLocal.withInitial(CancelHandler::new);

    public static Cancelable getQueryCancelHandler() {
        return CANCEL_HANDLER.get();
    }

    public StatementHandlerWrapper(StatementHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
        CancelHandler cancelHandler = CANCEL_HANDLER.get();
        try {
            cancelHandler.lastStatement = statement;
            return wrapped.query(statement, resultHandler);
        } finally {
            cancelHandler.lastStatement = null;
        }
    }

    // implement all other methods by just delegating to the wrapped handler
}

Последний шаг - создать свой плагин:

public class StatementHandlerInterceptor implements org.apache.ibatis.plugin.Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return new StatementHandlerWrapper((StatementHandler) target);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {}
}

и зарегистрировать его в своем mapping.xml, где вы определяете все свои отображения iBatis:

<configuration>
  <plugins>
    <plugin interceptor="com.example.StatementHandlerInterceptor" />
  </plugins>
  <mappers>
    <!-- all your mappers go here -->
  </mappers>
<configuration>

Чтобы использовать этот небольшой фреймворк,мы можем создать java.util.concurrent.Callable:

public class QueryTask implements Callable<List<?>>, Cancelable {

    private Cancelable cancelHandler;

    @Override
    public List<?> call() throws Exception {
        cancelHandler = StatementHandlerWrapper.getQueryCancelHandler();

        // execute your query here and return the result
        return null;
    }

    @Override
    public void cancel() {
        if (cancelHandler != null) {
            cancelHandler.cancel();
        }
    }
}

И основной поток может выглядеть следующим образом:

QueryTask queryTask = new QueryTask();
Future<List<?>> futureResult = Executors.newSingleThreadExecutor().submit(queryTask);
// either cancel or get the result
queryTask.cancel();
List<?> result = futureResult.get();

Если вы найдете более простое решение, пожалуйста, напишите комментарий.

0 голосов
/ 06 июля 2011

Вы пытались получить ссылку на соединение, на котором выполняется оператор, и сделать соединение недействительным?

...