Skip to the content.

今天想做一个导出excel的功能,杀掉长时间没有反应的查询,最开始的思路是通过concurrent包使用子线程执行,在主线程通过future在超时后取消掉查询。

其他线程kill实现方式

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<List<List<String>>> future = executor
    .submit(new Callable<List<List<String>>>() {
        @Override
        public List<List<String>> call() throws Exception {
            return excelService.getBySql(safeSql);
        }   
    }); 
log.info("after " + String.valueOf(second) + "second");
try {
	List<List<String>> datas = future.get(second, TimeUnit.SECONDS);
    map = createAjaxSuccessMap();
	map.put("datas", datas);
	map.put("sql", safeSql);
} catch (TimeoutException te) {
	excelService.cancelQuery();
    future.cancel(true);
    map = createAjaxFailureMap("查询超时"); 
}

public void cancelQuery() {
	hibernateTemplate.execute(new HibernateCallback<Object>() {
    	@Override
        public Object doInHibernate(Session session) throws HibernateException, SQLException {
            session.cancelQuery();
	        return null;
    	}   
    }); 
}

后来通过mysql客户端show processlist查看当前的连接,发现cancelQuery不好使,查询依然继续执行。

hibernate cancelQuery方法上的注释说 This is the sole method on session which may be safely called from another thread.(这是session提供的唯一一个可以通过另外一个线程调用的方法)

也找到了作者写这个方法的需求的源头http://www.java2s.com/Questions_And_Answers/JPA/Query/cancel.htm

但是查询仍然没有取消,最后还是找到mysql jdbc驱动官方文档说明发现了问题的根源,取消查询有两种方式:

Timeout实现

protected List<Object[]> findBySql(final String sql, final int timeout) {
    return (List<Object[]>) hibernateTemplate.executeFind(new HibernateCallback() {
        @Override
        public Object doInHibernate(Session session) throws HibernateException, SQLException {
	        SQLQuery sqlQuery = session.createSQLQuery(sql);
	        sqlQuery.setTimeout(timeout);
        	return sqlQuery.list();
    	}   
	}); 
}

TODO

对于上面的代码而言,其实关键在于子线程中session和主线程中的session是否是使用的一个connection,这就要看hibernate的实现了(TODO:详细了解一下hibernate中session的分配)。