参考过文章工作笔记-Spring5的坑
他方法里提到的解决办法是
然鹅这样并不根本解决办法,因为我的程序还会因为session 为空导致后续的查询或者数据库操作报错
我的 ======>>>>>>Hibernate 5.4.21.Final | Spring 5.2.9.RELEASE
出现的地方大概在:
ImportService 加了 @Transactional的注解,里面有个方法叫 importExcelAsync ,
然后方法里面Thread 调用了ExcelHandler的方法,ExcelHandler 一个方法叫saveBatch(从一开始就加了注解@Transactional)
一个方法叫doAfterAllAnalysed 一开始没加
我的项目需求就是上传文件,会创建一条记录,初始化状态为1 ;当读取完数据之后,这条数据的状态会变成2;同样需要实现异步读取文件,所以用到的是Thread
详细可查阅另一篇文章关于FastExcel 的使用
实现的代码大概如下:
@Transactional
public class ImportSheetService extends BaseHibernateDao {
private ExecutorService executorService = Executors.newFixedThreadPool(20);
@Resource
private ExcelHandler alipayExcelHandler;
public Map<String, Object> importExcelAsync(Long userId, MultipartFile file) throws IOException, BaseException {
//此处省略一部分代码…………
//执行异步读取表格数据
executorService.submit(() -> {
FastExcel.read(inputStream, AlipayImportDetailStat.class, alipayExcelHandler).headRowNumber(headRowNumber.get())
.charset(Charset.forName(charset)) .sheet().doRead();
logger.info("read Excel");
});
try {
//等待所有任务完成
Map<String, Object> map = new HashMap<>();
//……
return map;
} finally {
// return null;
}
}
//读取完成后更新
public void finished(ImportSheet sheet,Long totalRows) {
if (sheet != null) {
try {
//此处省略保存代码……
} catch (BaseException e) {
throw new PersistentException(ErrorCode.OBJECT_ADD_ERROR,"增加对象失败!",e);
}
}else{
logger.error("sheet empty");
}
}
}
@Component
public class ExcelHandler extends AnalysisEventListener<ImportDetailStat> {
private static final Log logger = LogFactory.getLog(AlipayExcelHandler.class);
//创建一个线程池,用于异步保存数据
private ExecutorService executorService = Executors.newFixedThreadPool(20);
//创建一个线程安全的list,用于存储读取到的数据,使用ThreadLocal保证线程安全
private ThreadLocal<ArrayList<ImportDetailStat>> sheetDataList = ThreadLocal.withInitial(ArrayList::new);
//用于统计是第几次插入
private static AtomicInteger count = new AtomicInteger(1);
private static AtomicInteger totalCount = new AtomicInteger(0);
@Resource
private ExcelHandler ExcelHandler;
@Resource
private ImportSheetService importSheetService;
public Long getTotalCount() {
return totalCount.longValue();
}
@Transactional
public void saveBatch(List<AlipayImportDetailStat> sheetDetailsList) throws BaseException {
if (!sheetDetailsList.isEmpty()) {
//省略保存代码……
}
}
//在这个方法上也需要加上@Transactional 就是正确的
@Override
@Transactional
public void doAfterAllAnalysed(AnalysisContext context) {
asyncSaveData();
try {
sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//处理结束之后,更新数据
importSheetService.finished();
}
处理的方式就是
在ExcelHandler 两个数据保存的方法上都需要加上@Transactional 的注解
此处用到的Transactional 是import javax.transaction.Transactional;
至于org.springframework.transaction.annotation.Transactional 与 javax.transaction.Transactional 差别,我就不赘述了;