需求
在【临时数据提取】项目中,存在需求:程序执行用户提供的sql时,如果这条sql查询的方法执行超过(从数据库配置表获取)秒,就终止这个方法。
解决方案
用Java线程池ExecutorService类配合Future接口来实现。Future接口是Java线程Future模式的实现,可以来进行异步计算。
代码
DataExtrComHandler :
package com.sfpay.console.util;
......
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
......
/**
* 类说明:<br>
* 临时数据提取模块Handler
* <p>
* 详细描述:<br>
* Common Util
* </p>
* @author 80003614
* CreateDate: 2018-12-3
*/
@Component("dataExtrComHandler")
public class DataExtrComHandler {
......
/**
* 执行sql查询,加载查询内容到内存
* @param connection 数据库连接对象
* @param querySql 要执行的sql语句
* @return
* @throws Exception
*/
public List<Object[]> executeDBQuery(final Connection connection, final String querySql) throws Exception {
// 该处需防止内存溢出,后期应优化。
// ArrayListHandler:将查询的结果,每一行先封装到Object数组中,然后将数据存入List集合
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<List<Object[]>> future = new FutureTask<List<Object[]>>(new Callable<List<Object[]>>() {
@Override
public List<Object[]> call() throws Exception {// 执行sql
QueryRunner queryRunner = new QueryRunner();
List<Object[]> data = queryRunner.query(connection, querySql, new ArrayListHandler());
// Thread.sleep(11000);
// int testNum = 1/0;
// System.out.println(testNum);
return data;
}
});
String exeSqlTimeout = null;
try {
executor.execute(future);
// 获取数据库规定的query timeout值
exeSqlTimeout = sysParaCfgMapper.selectParaValue("EXE-SQL-TIMEOUT-DURATION");
List<Object[]> result = future.get(Long.parseLong(exeSqlTimeout), TimeUnit.MILLISECONDS);
return result;
} catch (TimeoutException e) { // sql执行超时
throw new TimeoutException(
"Sql执行超时!系统规定Sql执行时间不能超过" + (Long.parseLong(exeSqlTimeout) / 1000 / 60) + "分钟...");
} catch (Exception e) {
throw new RuntimeException("Sql执行异常:" + e.getMessage());
} finally {
future.cancel(true);
executor.shutdown();
}
}
......
}