在写上传文件,异步解析Excel数据时,老是出现“Transaction was marked for rollback only; cannot commit”错误,卡了很长一段时间。今天终终于于解决,发现是事务嵌套的情况下导致的问题。
最外层Controller调用了procesService的create()方法创建进程:
//创建上传文件进程
procesService.create(file, userId);
create中调用了proxyService的asyncProcessFiles()异步处理上传的数据:
//异步解析上传的Excel文件
proxyService.asyncProcessFiles(process.getId());
asyncProcessFiles中调用了sampleService中的batchImport()执行具体的数据解析动作
sampleService.batchImport(processId);
最初三个方法都加了@Transactional事务控制,发现数据重复时总是提交不成功,于是去掉最里面两层的@Transactional,测试发现还是不行,本意是首先创建上传文件进程,在数据解析成功入库后,上传文件进程的状态置为1,否则状态为0。但在这种情况下,外层create事务正常提交依旧报错,上传进程也会创建失败。于是将create的@Transactional也去掉,每层向上抛出异常(throws),测试成功,问题解决。
原因:
一个事务方法A调用另一个事务方法B时,B如果报错事务就已经是回滚状态了,放回到A之后,A方法继续执行,提交了事务(已经是回滚状态的事务),就报错了 Transaction marked as rollback only。
在spring中,在事务方法中调用多个事务方法时,spring将会把这些事务合二为一。当整个方法中每个子方法没报错时,整个方法执行完才提交事务,如果某个子方法有异常, spring将该事务标志为rollback only。如果这个子方法没有将异常往上整个方法抛出或整个方法未往上抛出(只在调用时catch住了内层异常),那么该异常就不会触发事务进行回滚,事务就会在整个方法执行完后提交,这时就会造成Transaction rolled back because it has been marked as rollback-only异常。如果我们往上抛了该异常,spring就会获取异常,并执行回滚。
参考https://blog.csdn.net/weixin_39407754/article/details/105222531