关于Transaction rolled back because it has been marked as rollback-only的报错分析
执行删除操作时使用事务报错,报错信息如下
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
代码分析
调用代码如下:简单描述一下业务逻辑,数据库实体中存在订购单元,因此在执行删除数据库时需要先删除订购单元,删除成功后再删除数据库。
@Override
public boolean delete(Long id) {
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(TransactionDefinition.withDefaults());
int result = 0;
try {
//-------------业务逻辑开始-------------
//删除订购单元
List<EbookCollectionUnitBean> collectionUnitBeans = ebookCollectionUnitMapper.selectList(Wrappers.<EbookCollectionUnitBean>lambdaQuery()
.eq(EbookCollectionUnitBean::getEbookCollectionId, id));
if (CollectionUtils.isNotEmpty(collectionUnitBeans)) {
collectionUnitBeans.stream().forEach(x -> eBookCollectionUnitService.delete(x.getId()));
}
//删除数据库
result = ebookCollectionMapper.deleteById(id);
//-------------业务逻辑结束-------------
//事务提交
dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
dataSourceTransactionManager.rollback(transactionStatus);
e.printStackTrace();
throw new BusinessException(e.getMessage());
}
return result > 0;
}
上面的业务代码中调用删除订购单元的程序:eBookCollectionUnitService.delete(x.getId()))
@Override
public Boolean delete(Long id) {
boolean result = false;
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(TransactionDefinition.withDefaults());
try {
EbookCollectionUnitBean ebookCollectionUnitBean = ebookCollectionUnitMapper.selectById(id);
/**
* 如果 数字资源采购库 的收录时间修改,则更改所属期刊下的所有文章 标记为馆藏资源
*/
if (ebookCollectionUnitBean.getPurchaseFlag() == 1) {
updateUnitPaperSource(ebookCollectionUnitBean, null);
}
result = ebookCollectionUnitMapper.deleteById(id) > 0;
dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
log.error("===delete error info", e);
dataSourceTransactionManager.rollback(transactionStatus);
}
return result;
}
问题分析
直接说结论,第二段的代码在更新数据库时报错导致事务回滚。
第一段代码中的事务执行到
dataSourceTransactionManager.commit(transactionStatus);
的时候就会发现
Transaction rolled back because it has been marked as rollback-only(事务已经是回滚状态)因为它被标记成了只回滚,所以只能执行select查询,insert/update/delete操作必然回滚
此时执行事务提交
dataSourceTransactionManager.commit(transactionStatus);
必然会报错,因此被catch捕捉到,同时catch中又有事务回滚的代码
dataSourceTransactionManager.rollback(transactionStatus);
所以又会引发第二个报错:
Transaction is already completed - do not call commit or rollback more than once per transaction (每个事务调用提交或回滚的次数不要超过一次)
问题解决
可知由于第二段代码中报错导致事务回滚,修改业务代码即可解决此问题。此时如果调用不报错,那么第一段代码即可成功进行事务提交,第二个报错也会随之解决。