定時任務裡面事務不生效問題

 不将就鸭發表於2024-09-10

下面是一個定時任務中事務不生效的場景。 報出的異常是: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("清理積分過程中發生異常");
		}
	}

相關文章