需求:
客户导入文件,先插入一条数据到导入记录表,然后给客户一个反馈 (导入中,请稍后查看) ,中间调用一个异步方法——执行具体的文件解析,数据存储
问题一:怎样成功调用异步方法?
方案一:另外写一个类B里面有异步方法,在类A里面注入B类,用时直接调用B的异步方法即可;
方案二:@Lazy+自己注入自己 ,用时直接本类调用,避免了另外创建一个类放异步方法。
@Slf4j
public class aaa implements AAA{
/**
* 为了使在同一个类中的异步方法生效 故加@Lazy注解 并且使用本类调用异步方法
*/
@Resource
@Lazy
private aaa aaatService;
/**
* 导入excel
*
* @param file
*/
@Override
public void achieveImportExcel(MultipartFile file) {
//同步方法。。。
//调用异步方法
aaatService.XXX();
}
@Async
@Transactional(rollbackFor = Exception.class)
public void XXX() {
//异步方法业务代码。。。
}
}
问题二:异步方法抛出了异常需要回滚,回滚需要数据库插入一条记录,但是catch里面的数据库插入数据的代码也被回滚了;
解决方案:因为这个异步方法不论是try还是catch里面的数据库操作都是同一个数据库连接,最后决定catch里面的数据库操作抽取成一个异步方法,因为线程之间事务相互隔离,可以解决catch里面插入数据失败问题。
@Async
@Transactional(rollbackFor = Exception.class)
public void XXX() {
//异步方法业务代码。。。
try{
//数据库操作。。。
}catch (Exception e){
//调用异步方法完成数据库操作
aaaService.yyy();
}
/**
* 异步设置记录失败状态
* @param record
* @param importExcelInsertFailed
*/
@Async
public void yyy() {
//数据库操作,插入一条记录
}
}
问题三:同步方法执行完业务代码,开启子线程执行业务代码,没等创建子线程,同步方法结束了,导致异步方法还没开始就结束了。。。
解决方案:使用sleep方法,让同步方法的主线程睡一小会儿,给它一个创建子线程的时间即可。
/**
* 改造同步方法
*
* @param file
*/
@Override
public void achieveImportExcel(MultipartFile file) {
//同步方法。。。
//调用异步方法
aaatService.XXX();
//线程睡眠,让异步方法有时间创建子线程执行任务
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
问题四:嵌套事务回滚多了
因为doOtherThing方法出现了异常,没有手动捕获,会继续往上抛,到外层add方法的代理方法中捕获了异常。所以,这种情况是直接回滚了整个事务,不只回滚单个保存点。
解决方案:这里引入嵌套事务的内部回滚使用方法
可以将内部嵌套事务放在try/catch中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。
第四部分解决方案引用文章:
spring事务(注解 @Transactional )失效的12种场景_transactional不生效的场景_春天的早晨的博客-CSDN博客