情况: 我在主线程中做了一个删除操作,但是在子线程中还能查到,我使用了事务,并且还设置了事务的传播级别,可是还是不行。但通过询问大牛,我找到了以下几种方法:
1.方法一:在子线程的查询语句中使用 for update 。(未尝试)
for update是一种行级锁,又叫排它锁,一旦用户对某个行施加了行级加锁,则该用户可以查询也可以更新被加锁的数据行,其它用户只能查询但不能更新被加锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。
2.方法二:在主线程删除后直接提交。
```java
@Transactional
public void onlineConfirmImport(MultipartFile file, Integer agencyAgreementType, String importYear) {
String errorImport = "导入模板不正确";
// 获取上传数据
List<OnlineConfirmImportVO> loadExcelData = loadExcelData(file, agencyAgreementType);
//手动获取事务状态
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
//删除旧的年份数据
try {
//通过agencyAgreementType importYear 查到对应的线上确认数据
OnlineConfirmSearchDTO onlineConfirmSearchDTO = new OnlineConfirmSearchDTO();
onlineConfirmSearchDTO.setAgencyAgreementType(agencyAgreementType);
onlineConfirmSearchDTO.setImportYear(importYear);
List<OnlineConfirm> yearList = onlineConfirmDao.getList(onlineConfirmSearchDTO);
List<Long> ids = new ArrayList<>();
List<String> agencyAgreementCodes = new ArrayList<>();
yearList.forEach(onlineConfirm -> {
ids.add(onlineConfirm.getId());
agencyAgreementCodes.add(onlineConfirm.getAgencyAgreementCode());
});
if(!ids.isEmpty()) {
onlineConfirmDao.deleteByIds(ids);
}
if (!agencyAgreementCodes.isEmpty()) {
onlineManualDao.deleteByAgencyAgreementCodes(agencyAgreementCodes);
}
transactionManager.commit(transactionStatus);
}catch (Exception e){
transactionManager.rollback(transactionStatus);
throw new BusinessException("导入失败,数据异常");
}
//创建子线程去做操作...
}
3.方法二的优化。
在方法二中,我们删除完是直接提交事务的,但是我们在子线程或者是后续的业务中可能会出现异常,这时我们的数据都提交了,就相当于我们啥都没做直接把数据给他删了,这样是不对的。
所以我们在删除的时候,可以先不做物理删除,我们先给他做一个逻辑删除,同时使用ThreadLocal去保存这些被删除的数据。使用 try -catch -finally,若是出现异常,我们在catch中手动update这些数据让他还原;若是没有出现异常,我们再在finally里取出ThreadLocal中的那些数据,将他们真正的逻辑删除。