我需要一个解决方案来取消长时间运行的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的)。
答案 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();
}
}
}
如果找到更简单的解决方案,请发表评论。