如何取消iBatis准备好的声明?

时间:2011-03-03 11:37:07

标签: ibatis aspectj

我需要一个解决方案来取消长时间运行的select语句。 我使用的是Spring 3.0.2,iBatis 2.3.0和Oracle 10g。 我设法让它与纯JDBC一起工作,但由于select是通过高级搜索屏幕动态生成的,我真的需要使用iBatis。

负责从预准备语句的缓存中创建/检索的iBatis内部类是 com.ibatis.sqlmap.engine.execution.SqlExecutor 。每次调用 queryForList()/ queryForObject()时调用的内部方法是SqlExecutor的

public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException 方法

由于性能原因,只有当给定的select语句不存在时,iBatis才会创建一个新的预准备语句。 准备好的语句存储/缓存在HashMap中,其中sql字符串是键,准备好的语句是值。

在尝试不同的其他解决方案但没有成功之后,我认为可以使用AOP( AspectJ )尝试切入SqlExecutor.executeQuery()方法并以某种方式存储在HTTP会话上iBatis缓存映射和sql字符串。

当用户尝试取消长时间运行的查询时,将从另一个线程进行检查,以查看先前存储在HTTP会话中的给定sql字符串的iBatis缓存映射中是否已存在预准备语句AOP。 如果存在,将发出Statement.cancel()调用。 我不明白为什么这样的解决方案可能会干扰iBatis内部机制,因为如果准备好的语句将被取消,将抛出SqlException( ORA-01013用户请求取消当前操作)和Ibatis将正确处理任何其他生成的SqlException。

使用 Spring AOP 不是一个选项,因为它只允许切入由Spring容器管理的对象中声明的切入方法。我不能将SqlExecutor声明为Spring bean,因为它是由iBatis在内部创建和管理的。

尚未尝试使用AspectJ的上述解决方案,因为我不太熟悉AspectJ框架。

我不确定这是否是正确的方法,但我没有找到另一种解决方案来取消由iBatis创建的预备语句,因为iBatis似乎没有提供任何支持(已经检查过) MyBatis的)。

2 个答案:

答案 0 :(得分:0)

您是否尝试过对正在运行该语句的连接的引用,并使连接无效?

答案 1 :(得分:0)

我知道,这个问题很旧,但是也许有人会觉得有用。

您可以使用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) {}
}

并在您定义所有iBatis映射的{​​{1}}中注册它:

mapping.xml

要使用这个小框架,我们可以创建一个<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();
        }
    }
}

如果找到更简单的解决方案,请发表评论。