java中避免集合死鏈調用詳情
1. 前言
開發過程中, 一些集合 的變動會觸發任務去 改變 其他的集合 ,為瞭保障任務的正確執行,應避免出現死循環調用,即對 集合之間的影響關系 進行一些限制。怕日後遺忘,特在此記錄。
2. 場景
- A 集合影響 A 集合。
- A 集合影響 B 集合,B 集合影響瞭 A 集合。
- A 集合影響 B 集合,B 集合影響瞭 C 集合,C 集合影響瞭 A 集合。
- A 集合影響 B 集合、C 集合,B 集合影響瞭 D 集合,C 集合影響瞭 E 集合,E 集合影響 A 集合。
3. 環境
3.1 開發環境準備
- JDK 1.8
- SpringBoot 2.x
- Mysql 8
- redis
3.2 數據準備
3.2.1 Mysql數據庫表及數據
dp_process表
CREATE TABLE `dp_process` ( `ID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'ID', `NAME` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名稱', `CODE` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '代碼', `CATEGORY` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '類型 1=樓宇,2=房地產', `IN_COLS` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '輸入集合', `OUT_COLS` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '影響集合', `REMARK` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '備註', `ENABLED` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否開啟', `STATUS` int DEFAULT NULL COMMENT '狀態 數據狀態:0=正常,1=刪除,失效', `CREATED_BY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '創建人', `CREATED_TIME` datetime DEFAULT NULL COMMENT '創建時間', `UPDATED_BY` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人', `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新時間', `REVISION` int DEFAULT '0' COMMENT '樂觀鎖', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='數據處理 ';
dp_process 表中的數據
INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('1', 'B', 'B', 'ly', 'A', 'B', 'B', '1', 0, NULL, NULL, NULL, NULL, 0); INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('2', 'D', 'D', 'ly', 'B', 'D', 'D', '1', 0, NULL, NULL, NULL, NULL, 0); INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('3', 'E', 'E', 'ly', 'B', 'E', 'E', '1', 0, NULL, NULL, NULL, NULL, 0); INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('4', 'G', 'G', 'ly', 'D', 'G', 'G', '1', 0, NULL, NULL, NULL, NULL, 0); INSERT INTO `gccs`.`dp_process`(`ID`, `NAME`, `CODE`, `CATEGORY`, `IN_COLS`, `OUT_COLS`, `REMARK`, `ENABLED`, `STATUS`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `REVISION`) VALUES ('5', 'F', 'F', 'ly', 'D', 'F', 'F', '1', 0, NULL, NULL, NULL, NULL, 0);
3.2.2 redis庫數據
key | Value |
---|---|
A | [{ “id”: “1”,”outCols”: “B”}] |
B | [{“id”: “2”,”outCols”: “D”},{“id”: “3”,”outCols”: “E”}] |
D | [{“id”: “4”,”outCols”: “G”},{“id”: “5”,”outCols”: “F”}] |
4. 解決方式
通過遞歸的方式循環查詢、對比。
本例主要牽扯到的知識點有:
Stack
(棧,先進後出)- 遞歸
redis
簡單增刪操作
本文以 修改方法 代碼為例,介紹如何實現防死鏈調用,非常簡單。
/** * @create 2021-07-08 更新 數據處理 * @param dpProcess 數據處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @return */ @Override public int modify(DpProcess dpProcess, String updateNil){ // **省略一堆代碼** // 輸入集合統一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // **省略一堆代碼** }
operInclos()
方法 : 重點 ,主要做瞭數據校驗、redis中數據更新等一系列操作
/** * @create 輸入集合統一處理 2021/7/11 14:13 * @param dpProcess 新數據處理對象 * @param oldClos 原數據處理對象中的輸入集合 * @return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數據處理對象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數據處理對象中的輸入集合沒有值,則直接跳過,不進行操作 if(StringUtils.isNotBlank(inCols)){ if(dpProcess.getInCols().contains(dpProcess.getOutCols())){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 數據類型轉換 Set<String> set = new HashSet(Arrays.asList(inCols.split(","))); // 循環遍歷輸入集合 for (String inClo : set) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當前集合的影響關系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲的集合影響關系不為空,做簡單的遍歷去重處理 if(StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲的集合影響關系列表 List<DpProcessVo> children = new ArrayList<>(); // 進行數據類型轉換 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); for (DpProcessVo dpProcessVo1 : children) { if(dpProcess.getId().equals(dpProcessVo1.getId())){ continue; } childFinalList.add(dpProcessVo1); } // 添加本次影響的集合 DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 如果redis中沒有此輸入集合的影響關系,則可以直接進行添加 else{ DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack.add(dpProcessVoTop); // 遍歷需要進行死鏈校驗的數據 for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標識(默認為添加,如果集合為死鏈,則進行提示) boolean addFlag = true; // 循環遍歷棧 for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false; break; } } if(!addFlag){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo); // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象並將該對象作為此函數的值返回 nodeStack.pop(); } } // 處理需要刪除的集合 dealNeedDeleteCols(dpProcess, oldClos, set); // 獲取並設置最終的集合名稱 String finallyCols = StringUtils.join(set.toArray(), ","); dpProcess.setInCols(finallyCols); // 省略一堆更新redis的操作 } }
invaldClosInfo()
方法: 遞歸深度遍歷
/** * @create 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return; } //獲得節點的子節點,對於二叉樹就是獲得節點的左子結點和右子節點 List<DpProcessVo> children = new ArrayList<>(); // redis中原來存儲的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關系 for (DpProcessVo dpProcessVo1 : children) { boolean addFlag = true; for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo1.getOutCols())){ addFlag = false; break; } } if(!addFlag){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象並將該對象作為此函數的值返回 nodeStack.pop(); } }
5.完整代碼
記錄代碼,方便日後復習、調用、重構。
5.1 Model
模型主要分兩部分:數據處理模型和簡化版的數據處理模型。
DpProcess:數據處理模型,數據完整的Sql操作
import com.alibaba.fastjson.annotation.JSONField; import com.baomidou.mybatisplus.annotation.*; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Date; /** * <p> * 數據處理 * </p> * * @since 2021-07-08 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="DpProcess對象", description="數據處理 ") @TableName("dp_process") public class DpProcess implements Serializable { @TableField(exist = false) public static final String ENABLED = "ENABLED"; @TableField(exist = false) public static final String STATUS = "STATUS"; @TableField(exist = false) public static final String CATEGORY = "CATEGORY"; private static final long serialVersionUID = 1L; @ApiModelProperty(value = "ID") @TableId(value = "ID", type = IdType.ASSIGN_ID) private String id; @ApiModelProperty(value = "名稱") @TableField("NAME") private String name; @ApiModelProperty(value = "代碼") @TableField("CODE") private String code; @ApiModelProperty(value = "類型 1=樓宇,2=房地產") @TableField("CATEGORY") private String category; @ApiModelProperty(value = "輸入集合") @TableField("IN_COLS") private String inCols; @ApiModelProperty(value = "影響集合") @TableField("OUT_COLS") private String outCols; @ApiModelProperty(value = "備註") @TableField("REMARK") private String remark; @ApiModelProperty(value = "是否開啟 0:否 1:是") @TableField("ENABLED") private String enabled; @ApiModelProperty(value = "狀態 數據狀態:0=正常,1=刪除,失效") @TableField(value = "STATUS", fill = FieldFill.INSERT) private Integer status; @ApiModelProperty(value = "創建人") @TableField(value = "CREATED_BY", fill = FieldFill.INSERT) private String createdBy; @ApiModelProperty(value = "創建時間") @JSONField(format = "yyyy-MM-dd HH:mm:ss") @TableField(value = "CREATED_TIME", fill = FieldFill.INSERT) private Date createdTime; @ApiModelProperty(value = "更新人") @TableField(value = "UPDATED_BY", fill = FieldFill.UPDATE) private String updatedBy; @ApiModelProperty(value = "更新時間") @JSONField(format = "yyyy-MM-dd HH:mm:ss") @TableField(value = "UPDATED_TIME", fill = FieldFill.UPDATE) private Date updatedTime; @ApiModelProperty(value = "樂觀鎖") @Version @TableField(value = "REVISION", fill = FieldFill.INSERT) private Integer revision; }
DpProcessVo: 數據處理簡單模型,處理redis數據結構數據。
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="DpProcessVo對象", description="數據處理簡單模型 ") public class DpProcessVo{ @ApiModelProperty(value = "ID") private String id; @ApiModelProperty(value = "影響集合") private String outCols; }
5.2 Controller
updateNil:讓用戶選擇使用那種更新方式,也可以把接口一拆為二,主要看個人習慣。
/** * @create 2021-07-08 更新 數據處理 * @param dpProcess 數據處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @return */ @ApiOperation(value="更新",notes = "更新") @PostMapping("/modify") public Result modify( @ApiParam(name = "dpProcess", value = "數據處理 模型", required = true) @RequestBody DpProcess dpProcess, @ApiParam(name = "updateNil", value = "全字段更新(新增時此字段可以忽略): 是:Y 否:不傳或者隨意傳") @RequestParam(required = false) String updateNil) { int addResult = dpProcessService.modify(dpProcess, updateNil); if (addResult > 0) { return new Result(CommonCode.SUCCESS, "更新成功!"); } return new Result(CommonCode.FAIL, "更新失敗!"); }
5.3 Service
沒啥好說的,就是一個接口。
/** * @create 2021-07-08 更新 數據處理 * @param dpProcess 數據處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @return */ int modify(DpProcess dpProcess, String updateNil);
5.4 Service 實現類
DpRecord:數據處理記錄,不是本文重點,此處可直接忽略,相關說明 待 數據流程處理文章中提現。
/** * @create 2021-07-08 更新 數據處理 * @param dpProcess 數據處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @return */ @Override public int modify(DpProcess dpProcess, String updateNil){ if(dpProcess == null){ throw new ServiceException("數據處理模型不能為空!"); } // 走更新方法 // 通過id查詢數據處理 詳情 DpProcess orignDpProcess = this.detail(dpProcess.getId()); if(dpProcess == null){ throw new ServiceException("數據處理模型信息不能為空!"); } // 如果當前任務已存在,需要先進行取消 if("0".equals(dpProcess.getEnabled())){ if(defaultSchedulingConfigurer.hasTask(dpProcess.getId())){ defaultSchedulingConfigurer.cancelTriggerTask(dpProcess.getId()); } // 根據數據處理ID查看數據庫中是否有需要執行的數據處理記錄 DpRecord dpRecord = dpRecordService.getNeedExecRecordByDppId(dpProcess.getId()); // 如果數據處理記錄信息為空,則進行新增 if(dpRecord != null){ // 設置結束時間為當前時間 dpRecord.setEndTime(new Date()); // 運行失敗 dpRecord.setSucceed("2"); dpRecord.setFailedResult("用戶取消操作"); } // 對數據處理記錄進行更新或者保存 dpRecordService.addOrUpdate(dpRecord, null); } // 限制輸出集合不能為空 dpProcess.setOutCols(StringUtils.isNotBlank(dpProcess.getOutCols()) ? dpProcess.getOutCols() : orignDpProcess.getOutCols()); if(StringUtils.isBlank(dpProcess.getOutCols())){ throw new ServiceException("數據影響集合不能為空!"); } // 輸入集合統一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // 全字段更新 if(SystemConst.Whether.Yes.getCode().equals(updateNil)){ if(StringUtils.isBlank(dpProcess.getRemark())){ throw new ServiceException("數據處理備註不能為空!"); } // 備註不能小於20字 if(dpProcess.getRemark().length() < 20){ throw new ServiceException("數據處理備註不能小於20字!"); } return dpProcessMapper.alwaysUpdateSomeColumnById(dpProcess); } // 數據處理代碼自動填充 dpProcess.setCode(StringUtils.isBlank(dpProcess.getCode()) ? orignDpProcess.getCode() : dpProcess.getCode()); return dpProcessMapper.updateById(dpProcess); }
operInclos() : 處理輸入集合的方法
/** * @create 輸入集合統一處理 2021/7/11 14:13 * @param dpProcess 新數據處理對象 * @param oldClos 原數據處理對象中的而輸入集合 * @return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數據處理對象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數據處理對象中的輸入集合沒有值,則直接跳過,不進行操作 if(StringUtils.isNotBlank(inCols)){ if(dpProcess.getInCols().contains(dpProcess.getOutCols())){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 數據類型轉換 Set<String> set = new HashSet(Arrays.asList(inCols.split(","))); // 循環遍歷輸入集合 for (String inClo : set) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當前集合的影響關系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲的集合影響關系不為空,做簡單的遍歷去重處理 if(StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲的集合影響關系列表 List<DpProcessVo> children = new ArrayList<>(); // 進行數據類型轉換 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); for (DpProcessVo dpProcessVo1 : children) { if(dpProcess.getId().equals(dpProcessVo1.getId())){ continue; } childFinalList.add(dpProcessVo1); } // 添加本次影響的集合 DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 如果redis中沒有此輸入集合的影響關系,則可以直接進行添加 else{ DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack.add(dpProcessVoTop); // 遍歷需要進行死鏈校驗的數據 for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標識(默認為添加,如果集合為死鏈,則進行提示) boolean addFlag = true; // 循環遍歷棧 for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false; break; } } if(!addFlag){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo); // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象並將該對象作為此函數的值返回 nodeStack.pop(); } } // 處理需要刪除的集合 dealNeedDeleteCols(dpProcess, oldClos, set); // 獲取並設置最終的集合名稱 String finallyCols = StringUtils.join(set.toArray(), ","); dpProcess.setInCols(finallyCols); // 能走到這一步,說明所有的集合沒有問題,可以進行更新操作瞭(再一次遍歷是為瞭和上面的校驗分開,避免部分數據被更新) for (String inClo : set) { List<DpProcessVo> dpProcessVoList = new ArrayList<>(); // 首先獲取當前集合影響的數據處理對象 String dpProcessJson = (String) redisUtil.get(inClo); if(StringUtils.isBlank(dpProcessJson)){ DpProcessVo dpProcessVo = new DpProcessVo(); dpProcessVo.setId(dpProcess.getId()); dpProcessVo.setOutCols(dpProcess.getOutCols()); dpProcessVoList.add(dpProcessVo); // 進行數據的存儲 redisUtil.set(inClo, JSONArray.toJSON(dpProcessVoList).toString()); continue; } // redis中原來存儲的信息 List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 把數據處理對象轉換為HashSet HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos); // 當前集合影響的 其他集合列表 List<DpProcessVo> childFinalList = new ArrayList<>(); // 遍歷redis中存儲的集合影響關系,並進行簡單去重處理 for (DpProcessVo dpProcessVo : hashSet) { if(dpProcessVo.getId().equals(dpProcess.getId())){ continue; } childFinalList.add(dpProcessVo); } // 添加上本次影響的集合 DpProcessVo dpProcessVo = new DpProcessVo(); dpProcessVo.setId(dpProcess.getId()); dpProcessVo.setOutCols(dpProcess.getOutCols()); // 添加當前數據數據對象 childFinalList.add(dpProcessVo); // 進行數據的存儲 redisUtil.set(inClo, JSONArray.toJSON(childFinalList).toString()); } } }
invaldClosInfo() : 驗證數據處理流程配置輸入流程是否調用瞭輸出集合
/** * @create 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return; } //獲得節點的子節點,對於二叉樹就是獲得節點的左子結點和右子節點 List<DpProcessVo> children = new ArrayList<>(); // redis中原來存儲的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關系 for (DpProcessVo dpProcessVo1 : children) { boolean addFlag = true; for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo1.getOutCols())){ addFlag = false; break; } } if(!addFlag){ throw new ServiceException("數據處理流程配置輸入流程調用瞭輸出集合!"); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗證數據處理流程配置輸入流程是否調用瞭輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象並將該對象作為此函數的值返回 nodeStack.pop(); } }
dealNeedDeleteCols() : 主要處理–原數據為 A 集合影響 B 集合,修改為 C 集合影響瞭 B 集合,此時需要刪除 A 對 B的影響關系
/** * @create 處理需要刪除的集合 2021/7/20 17:58 * @param dpProcess 數據處理模型 * @param oldClos 原來的數據處理模型中的集合信息 * @param set 最新的集合名稱信息 * @return void */ private void dealNeedDeleteCols(DpProcess dpProcess, String oldClos, Set<String> set) { if(StringUtils.isBlank(oldClos)){ return; } // 獲取去重後的集合數組 List<String> newColsList = new ArrayList<>(set); // 原來的集合數組 List<String> oldColsList = Arrays.asList(oldClos.split(",")); // 獲取兩個集合的差集 List<String> reduceList = oldColsList.stream().filter(item -> !newColsList.contains(item)).collect(toList()); if(reduceList == null || reduceList.size() == 0){ return; } for (String clos : reduceList) { // 獲取redis中的集合 String dpProcessJson = (String) redisUtil.get(clos); if(StringUtils.isBlank(dpProcessJson)){ continue; } // redis中原來存儲的信息 List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷刪除的集合中影響的流程ID HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos); Iterator<DpProcessVo> it = hashSet.iterator(); while(it.hasNext()){ DpProcessVo dpProcessVo = it.next(); if(dpProcessVo.getId().equals(dpProcess.getId())){ it.remove(); } } // 如果當前集合影響的流程為空,則進行刪除 if(hashSet.isEmpty()){ // 進行數據的存儲 redisUtil.delete(clos); continue; } // 進行數據的存儲 redisUtil.set(clos, JSONArray.toJSON(hashSet.toArray()).toString()); } }
6.測試
可通過單元測試等多種方式,本文提供簡單的測試數據。
{ "category": "ly", "code": "F", "createdBy": "", "createdTime": null, "enabled": "1", "id": "5", "inCols": "D", "name": "F", "outCols": "L", "remark": "F", "revision": 0, "status": 0, "updatedBy": "", "updatedTime": null }
到此這篇關於java中避免集合死鏈調用詳情的文章就介紹到這瞭,更多相關java中避免集合死鏈調用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Vue elementUI實現樹形結構表格與懶加載
- Mybatis-Plus自動填充更新操作相關字段的實現
- JAVA如何把數據庫的數據處理成樹形結構
- SpringBoot實現阿裡雲快遞物流查詢的示例代碼
- SpringBoot集成EasyExcel的應用場景分析