1. 前言
開發(fā)過程中, 一些集合 的變動會觸發(fā)任務(wù)去 改變 其他的集合 ,為了保障任務(wù)的正確執(zhí)行,應(yīng)避免出現(xiàn)死循環(huán)調(diào)用,即對 集合之間的影響關(guān)系 進(jìn)行一些限制。怕日后遺忘,特在此記錄。
2. 場景
- A 集合影響 A 集合。
- A 集合影響 B 集合,B 集合影響了 A 集合。
- A 集合影響 B 集合,B 集合影響了 C 集合,C 集合影響了 A 集合。
- A 集合影響 B 集合、C 集合,B 集合影響了 D 集合,C 集合影響了 E 集合,E 集合影響 A 集合。
3. 環(huán)境
3.1 開發(fā)環(huán)境準(zhǔn)備
- JDK 1.8
- SpringBoot 2.x
- Mysql 8
- redis
3.2 數(shù)據(jù)準(zhǔn)備
3.2.1 Mysql數(shù)據(jù)庫表及數(shù)據(jù)
dp_process表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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=房地產(chǎn)' , `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 '狀態(tài) 數(shù)據(jù)狀態(tài):0=正常,1=刪除,失效' , `CREATED_BY` varchar (32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '創(chuàng)建人' , `CREATED_TIME` datetime DEFAULT NULL COMMENT '創(chuàng)建時間' , `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= '數(shù)據(jù)處理 ' ; |
dp_process 表中的數(shù)據(jù)
1
2
3
4
5
|
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庫數(shù)據(jù)
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. 解決方式
通過遞歸的方式循環(huán)查詢、對比。
本例主要牽扯到的知識點有:
-
Stack
(棧,先進(jìn)后出) - 遞歸
-
redis
簡單增刪操作
本文以 修改方法 代碼為例,介紹如何實現(xiàn)防死鏈調(diào)用,非常簡單。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @Override public int modify (DpProcess dpProcess, String updateNil){ // **省略一堆代碼** // 輸入集合統(tǒng)一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // **省略一堆代碼** } |
operInclos()
方法 : 重點 ,主要做了數(shù)據(jù)校驗、redis中數(shù)據(jù)更新等一系列操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
/** * @create 輸入集合統(tǒng)一處理 2021/7/11 14:13 * @param dpProcess 新數(shù)據(jù)處理對象 * @param oldClos 原數(shù)據(jù)處理對象中的輸入集合 * @return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數(shù)據(jù)處理對象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數(shù)據(jù)處理對象中的輸入集合沒有值,則直接跳過,不進(jìn)行操作 if (StringUtils.isNotBlank(inCols)){ if (dpProcess.getInCols().contains(dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 數(shù)據(jù)類型轉(zhuǎn)換 Set<String> set = new HashSet(Arrays.asList(inCols.split( "," ))); // 循環(huán)遍歷輸入集合 for (String inClo : set) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當(dāng)前集合的影響關(guān)系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲的集合影響關(guān)系不為空,做簡單的遍歷去重處理 if (StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲的集合影響關(guān)系列表 List<DpProcessVo> children = new ArrayList<>(); // 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換 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中沒有此輸入集合的影響關(guān)系,則可以直接進(jìn)行添加 else { DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList.add(dpProcess1); } // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設(shè)置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack.add(dpProcessVoTop); // 遍歷需要進(jìn)行死鏈校驗的數(shù)據(jù) for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標(biāo)識(默認(rèn)為添加,如果集合為死鏈,則進(jìn)行提示) boolean addFlag = true ; // 循環(huán)遍歷棧 for (DpProcessVo processVo : nodeStack) { if (processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false ; break ; } } if (!addFlag){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo); // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象并將該對象作為此函數(shù)的值返回 nodeStack.pop(); } } // 處理需要刪除的集合 dealNeedDeleteCols(dpProcess, oldClos, set); // 獲取并設(shè)置最終的集合名稱 String finallyCols = StringUtils.join(set.toArray(), "," ); dpProcess.setInCols(finallyCols); // 省略一堆更新redis的操作 } } |
invaldClosInfo()
方法: 遞歸深度遍歷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/** * @ create 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @ return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關(guān)系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return ; } //獲得節(jié)點的子節(jié)點,對于二叉樹就是獲得節(jié)點的左子結(jié)點和右子節(jié)點 List<DpProcessVo> children = new ArrayList<>(); // redis中原來存儲的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關(guān)系 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( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象并將該對象作為此函數(shù)的值返回 nodeStack.pop(); } } |
5.完整代碼
記錄代碼,方便日后復(fù)習(xí)、調(diào)用、重構(gòu)。
5.1 Model
模型主要分兩部分:數(shù)據(jù)處理模型和簡化版的數(shù)據(jù)處理模型。
DpProcess:數(shù)據(jù)處理模型,數(shù)據(jù)完整的Sql操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
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> * 數(shù)據(jù)處理 * </p> * * @since 2021-07-08 */ @Data @EqualsAndHashCode(callSuper = false ) @Accessors(chain = true ) @ApiModel(value= "DpProcess對象" , description= "數(shù)據(jù)處理 " ) @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=房地產(chǎn)" ) @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 = "狀態(tài) 數(shù)據(jù)狀態(tài):0=正常,1=刪除,失效" ) @TableField(value = "STATUS" , fill = FieldFill. INSERT ) private Integer status; @ApiModelProperty(value = "創(chuàng)建人" ) @TableField(value = "CREATED_BY" , fill = FieldFill. INSERT ) private String createdBy; @ApiModelProperty(value = "創(chuàng)建時間" ) @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: 數(shù)據(jù)處理簡單模型,處理redis數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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= "數(shù)據(jù)處理簡單模型 " ) public class DpProcessVo{ @ApiModelProperty(value = "ID" ) private String id; @ApiModelProperty(value = "影響集合" ) private String outCols; } |
5.2 Controller
updateNil:讓用戶選擇使用那種更新方式,也可以把接口一拆為二,主要看個人習(xí)慣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @ApiOperation(value= "更新" ,notes = "更新" ) @PostMapping( "/modify" ) public Result modify ( @ApiParam( name = "dpProcess" , value = "數(shù)據(jù)處理 模型" , 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
沒啥好說的,就是一個接口。
1
2
3
4
5
6
7
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ int modify (DpProcess dpProcess, String updateNil); |
5.4 Service 實現(xiàn)類
DpRecord:數(shù)據(jù)處理記錄,不是本文重點,此處可直接忽略,相關(guān)說明 待 數(shù)據(jù)流程處理文章中提現(xiàn)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
/** * @ create 2021-07-08 更新 數(shù)據(jù)處理 * @param dpProcess 數(shù)據(jù)處理 模型 * @param updateNil 全字段更新(新增時此字段可以忽略): 是:Y 否:N {@link SystemConst.Whether} * @ return */ @Override public int modify (DpProcess dpProcess, String updateNil){ if(dpProcess == null ){ throw new ServiceException( "數(shù)據(jù)處理模型不能為空!" ); } // 走更新方法 // 通過id查詢數(shù)據(jù)處理 詳情 DpProcess orignDpProcess = this.detail(dpProcess.getId()); if(dpProcess == null ){ throw new ServiceException( "數(shù)據(jù)處理模型信息不能為空!" ); } // 如果當(dāng)前任務(wù)已存在,需要先進(jìn)行取消 if( "0" .equals(dpProcess.getEnabled())){ if(defaultSchedulingConfigurer.hasTask(dpProcess.getId())){ defaultSchedulingConfigurer.cancelTriggerTask(dpProcess.getId()); } // 根據(jù)數(shù)據(jù)處理ID查看數(shù)據(jù)庫中是否有需要執(zhí)行的數(shù)據(jù)處理記錄 DpRecord dpRecord = dpRecordService.getNeedExecRecordByDppId(dpProcess.getId()); // 如果數(shù)據(jù)處理記錄信息為空,則進(jìn)行新增 if(dpRecord != null ){ // 設(shè)置結(jié)束時間為當(dāng)前時間 dpRecord.setEndTime(new Date ()); // 運(yùn)行失敗 dpRecord.setSucceed( "2" ); dpRecord.setFailedResult( "用戶取消操作" ); } // 對數(shù)據(jù)處理記錄進(jìn)行更新或者保存 dpRecordService.addOrUpdate(dpRecord, null ); } // 限制輸出集合不能為空 dpProcess.setOutCols(StringUtils.isNotBlank(dpProcess.getOutCols()) ? dpProcess.getOutCols() : orignDpProcess.getOutCols()); if(StringUtils.isBlank(dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)影響集合不能為空!" ); } // 輸入集合統(tǒng)一處理 operInclos(dpProcess, orignDpProcess.getInCols()); // 全字段更新 if(SystemConst.Whether.Yes.getCode().equals(updateNil)){ if(StringUtils.isBlank(dpProcess.getRemark())){ throw new ServiceException( "數(shù)據(jù)處理備注不能為空!" ); } // 備注不能小于20字 if(dpProcess.getRemark().length() < 20){ throw new ServiceException( "數(shù)據(jù)處理備注不能小于20字!" ); } return dpProcessMapper.alwaysUpdateSomeColumnById(dpProcess); } // 數(shù)據(jù)處理代碼自動填充 dpProcess.setCode(StringUtils.isBlank(dpProcess.getCode()) ? orignDpProcess.getCode() : dpProcess.getCode()); return dpProcessMapper.updateById(dpProcess); } |
operInclos() : 處理輸入集合的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
/** * @ create 輸入集合統(tǒng)一處理 2021/7/11 14:13 * @param dpProcess 新數(shù)據(jù)處理對象 * @param oldClos 原數(shù)據(jù)處理對象中的而輸入集合 * @ return */ private void operInclos(DpProcess dpProcess, String oldClos) { // 新數(shù)據(jù)處理對象中的輸入集合 String inCols = dpProcess.getInCols(); // 若新數(shù)據(jù)處理對象中的輸入集合沒有值,則直接跳過,不進(jìn)行操作 if(StringUtils.isNotBlank(inCols)){ if(dpProcess.getInCols(). contains (dpProcess.getOutCols())){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 數(shù)據(jù)類型轉(zhuǎn)換 Set <String> set = new HashSet(Arrays.asList(inCols.split( "," ))); // 循環(huán)遍歷輸入集合 for (String inClo : set ) { // 最終需要遍歷的list List<DpProcessVo> childFinalList = new ArrayList<>(); // 從redis中獲取當(dāng)前集合的影響關(guān)系 String dpProcessJson = (String) redisUtil.get(inClo); // 如果redis中存儲的集合影響關(guān)系不為空,做簡單的遍歷去重處理 if(StringUtils.isNotBlank(dpProcessJson)){ // redis中存儲的集合影響關(guān)系列表 List<DpProcessVo> children = new ArrayList<>(); // 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換 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中沒有此輸入集合的影響關(guān)系,則可以直接進(jìn)行添加 else { DpProcessVo dpProcess1 = new DpProcessVo(); dpProcess1.setId(dpProcess.getId()); dpProcess1.setOutCols(dpProcess.getOutCols()); childFinalList. add (dpProcess1); } // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 Stack<DpProcessVo> nodeStack = new Stack<>(); // 設(shè)置模型 DpProcessVo dpProcessVoTop = new DpProcessVo(); dpProcessVoTop.setOutCols(inClo); dpProcessVoTop.setId(dpProcess.getId()); nodeStack. add (dpProcessVoTop); // 遍歷需要進(jìn)行死鏈校驗的數(shù)據(jù) for (DpProcessVo dpProcessVo : childFinalList) { // 是否添加標(biāo)識(默認(rèn)為添加,如果集合為死鏈,則進(jìn)行提示) boolean addFlag = true ; // 循環(huán)遍歷棧 for (DpProcessVo processVo : nodeStack) { if(processVo.getOutCols().equals(dpProcessVo.getOutCols())){ addFlag = false ; break; } } if(!addFlag){ throw new ServiceException( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo); // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象并將該對象作為此函數(shù)的值返回 nodeStack.pop(); } } // 處理需要刪除的集合 dealNeedDeleteCols(dpProcess, oldClos, set ); // 獲取并設(shè)置最終的集合名稱 String finallyCols = StringUtils. join ( set .toArray(), "," ); dpProcess.setInCols(finallyCols); // 能走到這一步,說明所有的集合沒有問題,可以進(jìn)行更新操作了(再一次遍歷是為了和上面的校驗分開,避免部分?jǐn)?shù)據(jù)被更新) for (String inClo : set ) { List<DpProcessVo> dpProcessVoList = new ArrayList<>(); // 首先獲取當(dāng)前集合影響的數(shù)據(jù)處理對象 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); // 進(jìn)行數(shù)據(jù)的存儲 redisUtil. set (inClo, JSONArray.toJSON(dpProcessVoList).toString()); continue ; } // redis中原來存儲的信息 List<DpProcessVo> dpProcessVos = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 把數(shù)據(jù)處理對象轉(zhuǎn)換為HashSet HashSet<DpProcessVo> hashSet = new HashSet(dpProcessVos); // 當(dāng)前集合影響的 其他集合列表 List<DpProcessVo> childFinalList = new ArrayList<>(); // 遍歷redis中存儲的集合影響關(guān)系,并進(jìn)行簡單去重處理 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()); // 添加當(dāng)前數(shù)據(jù)數(shù)據(jù)對象 childFinalList. add (dpProcessVo); // 進(jìn)行數(shù)據(jù)的存儲 redisUtil. set (inClo, JSONArray.toJSON(childFinalList).toString()); } } } |
invaldClosInfo() : 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/** * @ create 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 2021/7/20 22:10 * @param nodeStack 深度遍歷棧 * @ return void */ public void invaldClosInfo(Stack<DpProcessVo> nodeStack) { // 查看此堆棧頂部的對象而不將其從堆棧中移除 DpProcessVo dpProcessVo = nodeStack.peek(); // 從redis中查找此集合影響的流程關(guān)系 String dpProcessJson = (String) redisUtil.get(dpProcessVo.getOutCols()); // 如果集合沒有影響其他集合,則直接返回 if(StringUtils.isBlank(dpProcessJson)){ return ; } //獲得節(jié)點的子節(jié)點,對于二叉樹就是獲得節(jié)點的左子結(jié)點和右子節(jié)點 List<DpProcessVo> children = new ArrayList<>(); // redis中原來存儲的信息 children = JSONArray.parseArray(dpProcessJson, DpProcessVo.class); // 遍歷集合影響的集合關(guān)系 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( "數(shù)據(jù)處理流程配置輸入流程調(diào)用了輸出集合!" ); } // 將dpProcessVo推到這個堆棧的頂部 nodeStack.push(dpProcessVo1); // 驗證數(shù)據(jù)處理流程配置輸入流程是否調(diào)用了輸出集合 invaldClosInfo(nodeStack); // 移除此堆棧頂部的對象并將該對象作為此函數(shù)的值返回 nodeStack.pop(); } } |
dealNeedDeleteCols() : 主要處理--原數(shù)據(jù)為 A 集合影響 B 集合,修改為 C 集合影響了 B 集合,此時需要刪除 A 對 B的影響關(guān)系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
/** * @ create 處理需要刪除的集合 2021/7/20 17:58 * @param dpProcess 數(shù)據(jù)處理模型 * @param oldClos 原來的數(shù)據(jù)處理模型中的集合信息 * @param set 最新的集合名稱信息 * @ return void */ private void dealNeedDeleteCols(DpProcess dpProcess, String oldClos, Set <String> set ) { if(StringUtils.isBlank(oldClos)){ return ; } // 獲取去重后的集合數(shù)組 List<String> newColsList = new ArrayList<>( set ); // 原來的集合數(shù)組 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(); } } // 如果當(dāng)前集合影響的流程為空,則進(jìn)行刪除 if(hashSet.isEmpty()){ // 進(jìn)行數(shù)據(jù)的存儲 redisUtil. delete (clos); continue ; } // 進(jìn)行數(shù)據(jù)的存儲 redisUtil. set (clos, JSONArray.toJSON(hashSet.toArray()).toString()); } } |
6.測試
可通過單元測試等多種方式,本文提供簡單的測試數(shù)據(jù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{ "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 } |
到此這篇關(guān)于java中避免集合死鏈調(diào)用詳情的文章就介紹到這了,更多相關(guān)java中避免集合死鏈調(diào)用內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/wgx519/p/15324635.html?utm_source=tuicool&utm_medium=referral