下面是一個定時任務中事務不生效的場景。 報出的異常是:Unexpected error occurred in scheduled task java.lang.RuntimeException: 清理積分過程中發生異常
@Scheduled(cron = "*/30 * * * * ? ")
public void cleanPoint() {
log.info("**********************************開始清理一年前這一天的使用者積分**********************************");
// 1. 獲取當前日期
LocalDate now = LocalDate.now();
log.info("當前日期:{}", now);
// 2. 獲取一年前的日期
LocalDate yearAgo = now.minusYears(1);
log.info("一年前的日期:{}", yearAgo);
// 3. 批次更新一年前這一天的資料,將其balance值設定為0
int pageNum = 1;
while (true) {
// 3.1 分批次更新user_assets_details表,每次5000條資料,將其balance設定為0
Page<UserAssetsDetailsCleanBO> page = new Page<>(pageNum, 500);
List<UserAssetsDetailsCleanBO> detailsList = userAssetsDetailsService.listInfo(yearAgo, page); // 查詢一年前這一天的資料(id, 使用者id,餘額之和,隆鑫積分之和,咕咕行積分之和)
if (CollectionUtils.isNotEmpty(detailsList)) {
cleanPoint(detailsList, page);
pageNum++;
} else {
break;
}
}
log.info("**********************************結束清理一年前這一天的使用者積分**********************************");
}
public void cleanPoint(List<UserAssetsDetailsCleanBO> detailsList, Page<UserAssetsDetailsCleanBO> page) {
log.info("**********************************開始清理一年前這一天的使用者積分**********************************");
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 3.1 分批次更新user_assets_details表,每次5000條資料,將其balance設定為0
log.info("成功查詢出了一年前這一天的資料,共{}條資料,採用分批處理,總共會執行{}次批次更新,當前是第{}次開始執行", detailsList.size(), page.getPages(), page.getCurrent());
log.info("當前是第{}次開始批次更新 user_assets_details 表", page.getCurrent());
if (CollectionUtils.isNotEmpty(detailsList)) {
List<UserAssetsDetails> newList = new ArrayList<>();
for (UserAssetsDetailsCleanBO bo : detailsList) {
UserAssetsDetails vo = new UserAssetsDetails();
vo.setId(bo.getId());
vo.setBalance(0);
newList.add(vo);
if (bo.getLoncinScore() == null) {
bo.setLoncinScore(0);
}
if (bo.getGuguxScore() == null) {
bo.setGuguxScore(0);
}
if (bo.getTotalScore() == null) {
bo.setTotalScore(0);
}
}
userAssetsDetailsService.updateBatchById(newList);
log.info("結束第{}次批次更新 user_assets_details 表", page.getCurrent());
log.info("當前是第{}次開始批次儲存 user_assets_bill 表", page.getCurrent());
// 3.2 將資料存入user_assets_bill使用者資產流水錶中
List<UserAssetsDetailsCleanBO> distinctDetailsList = detailsList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(UserAssetsDetailsCleanBO::getUserId))), ArrayList::new)); // 根據使用者id去重
if (CollectionUtils.isNotEmpty(distinctDetailsList)) {
for (UserAssetsDetailsCleanBO details : distinctDetailsList) {
Long userId = details.getUserId();
// 判斷該使用者是否已存入過資產流水錶中
boolean containsUserId = cacheService.isContainsUserId(userId);
if (containsUserId) {
continue;
}
Integer totalScore = details.getTotalScore();
Integer guguxScore = details.getGuguxScore();
Integer loncinScore = details.getLoncinScore();
boolean b = userScoreBusinessNewService.scoreExpired(userId, totalScore, guguxScore, loncinScore);
cacheService.setUserId(userId);
if (!b) {
log.error("使用者{}的積分清理失敗", userId);
}
}
}
log.info("結束第{}次批次儲存 user_assets_bill 表", page.getCurrent());
}
int a = 1/0;
} catch (Exception e) {
e.printStackTrace();
log.error("清理積分過程中發生異常", e);
throw new RuntimeException("清理積分過程中發生異常");
}
}
解決方案如下,手動事務:
// 手動事務
@Resource(name = "transactionManager")
private DataSourceTransactionManager transactionManager;
//@XxlJob("EverydayCleanPointJobHandler")
@Scheduled(cron = "*/30 * * * * ? ")
public void cleanPoint() {
log.info("**********************************開始清理一年前這一天的使用者積分**********************************");
// 1. 獲取當前日期
LocalDate now = LocalDate.now();
log.info("當前日期:{}", now);
// 2. 獲取一年前的日期
LocalDate yearAgo = now.minusYears(1);
log.info("一年前的日期:{}", yearAgo);
// 3. 批次更新一年前這一天的資料,將其balance值設定為0
int pageNum = 1;
while (true) {
// 3.1 分批次更新user_assets_details表,每次5000條資料,將其balance設定為0
Page<UserAssetsDetailsCleanBO> page = new Page<>(pageNum, 500);
List<UserAssetsDetailsCleanBO> detailsList = userAssetsDetailsService.listInfo(yearAgo, page); // 查詢一年前這一天的資料(id, 使用者id,餘額之和,隆鑫積分之和,咕咕行積分之和)
if (CollectionUtils.isNotEmpty(detailsList)) {
cleanPoint(detailsList, page);
pageNum++;
} else {
break;
}
}
log.info("**********************************結束清理一年前這一天的使用者積分**********************************");
}
public void cleanPoint(List<UserAssetsDetailsCleanBO> detailsList, Page<UserAssetsDetailsCleanBO> page) {
log.info("**********************************開始清理一年前這一天的使用者積分**********************************");
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 3.1 分批次更新user_assets_details表,每次5000條資料,將其balance設定為0
log.info("成功查詢出了一年前這一天的資料,共{}條資料,採用分批處理,總共會執行{}次批次更新,當前是第{}次開始執行", detailsList.size(), page.getPages(), page.getCurrent());
log.info("當前是第{}次開始批次更新 user_assets_details 表", page.getCurrent());
if (CollectionUtils.isNotEmpty(detailsList)) {
List<UserAssetsDetails> newList = new ArrayList<>();
for (UserAssetsDetailsCleanBO bo : detailsList) {
UserAssetsDetails vo = new UserAssetsDetails();
vo.setId(bo.getId());
vo.setBalance(0);
newList.add(vo);
if (bo.getLoncinScore() == null) {
bo.setLoncinScore(0);
}
if (bo.getGuguxScore() == null) {
bo.setGuguxScore(0);
}
if (bo.getTotalScore() == null) {
bo.setTotalScore(0);
}
}
userAssetsDetailsService.updateBatchById(newList);
log.info("結束第{}次批次更新 user_assets_details 表", page.getCurrent());
log.info("當前是第{}次開始批次儲存 user_assets_bill 表", page.getCurrent());
// 3.2 將資料存入user_assets_bill使用者資產流水錶中
List<UserAssetsDetailsCleanBO> distinctDetailsList = detailsList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(UserAssetsDetailsCleanBO::getUserId))), ArrayList::new)); // 根據使用者id去重
if (CollectionUtils.isNotEmpty(distinctDetailsList)) {
for (UserAssetsDetailsCleanBO details : distinctDetailsList) {
Long userId = details.getUserId();
// 判斷該使用者是否已存入過資產流水錶中
boolean containsUserId = cacheService.isContainsUserId(userId);
if (containsUserId) {
continue;
}
Integer totalScore = details.getTotalScore();
Integer guguxScore = details.getGuguxScore();
Integer loncinScore = details.getLoncinScore();
boolean b = userScoreBusinessNewService.scoreExpired(userId, totalScore, guguxScore, loncinScore);
cacheService.setUserId(userId);
if (!b) {
log.error("使用者{}的積分清理失敗", userId);
}
}
}
log.info("結束第{}次批次儲存 user_assets_bill 表", page.getCurrent());
}
int a = 1/0;
transactionManager.commit(status); // 手動提交事務
} catch (Exception e) {
e.printStackTrace();
log.error("清理積分過程中發生異常", e);
transactionManager.rollback(status); // 手動回滾事務
throw new RuntimeException("清理積分過程中發生異常");
}
}