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.1Model
模型主要分两部分:数据处理模型和简化版的数据处理模型。
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
}
7.总结
仅对今日工作进行简单记录,代码还需进一步重构,记录永不止步。
java中避免集合死链调用的更多相关文章
- Java中的集合框架-Map
前两篇<Java中的集合框架-Commection(一)>和<Java中的集合框架-Commection(二)>把集合框架中的Collection开发常用知识点作了一下记录,从 ...
- 万字长文深入理解java中的集合-附PDF下载
目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fail- ...
- 菜鸟日记之 java中的集合框架
java中的集合框架图 如图所示:java中的集合分为两种Collection和Map两种接口 可分为Collection是单列集合和Map的双列集合 Collection单列集合:继承了Iterat ...
- C#与java中的集合区别
集合一般的操作 插入: add 删除: remove 查找: contains,remove java中的集合 注意哪些是接口,哪些是实现类 使用集合的时候 1. ...
- java中的集合操作类(未完待续)
申明: 实习生的肤浅理解,如发现有错误之处.还望大牛们多多指点 废话 事实上我写java的后台操作,我每次都会遇到一条语句:List<XXXXX> list = new ArrayList ...
- Java中的集合概述
Java中的集合类有两个重要的分支,分别是接口Collection(包括List,Set等)和接口Map. 由于HashSet的内部实现原理使用了HashMap,所以我们先来了解Map集合类. 1.H ...
- Java中的集合框架(上)
Java中的集合框架概述 集合的概念: Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象. 集合的作用: 1.在类的内部,对数据进行组织: 2.简单的快速的搜索大数据量的 ...
- Java中Set集合是如何实现添加元素保证不重复的?
Java中Set集合是如何实现添加元素保证不重复的? Set集合是一个无序的不可以重复的集合.今天来看一下为什么不可以重复. Set是一个接口,最常用的实现类就是HashSet,今天我们就拿HashS ...
- Java中各种集合(字符串类)的线程安全性!!!
Java中各种集合(字符串类)的线程安全性!!! 一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到该线程读 ...
随机推荐
- 【笔记】初探KNN算法(3)
KNN算法(3) 测试算法的目的就是为了帮助我们选择一个更好的模型 训练数据集,测试数据集方面 一般来说,我们训练得到的模型直接在真实的环境中使用 这就导致了一些问题 如果模型很差,未经改进就应用在现 ...
- iOS开发之Lame编译
前言 为了保证音频格式在多端通用,需要将音频转化为MP3格式,本文讲解了如何使用Shell脚本来编译lame库. 编译脚本 #!/bin/sh CONFIGURE_FLAGS="--disa ...
- Elasticsearch核心技术(二):Elasticsearch入门
本文从基本概念.基本CRUD操作.倒排索引原理.分词等部分来初识Elasticsearch. 2.1 基本概念 Elasticsearch是面向文档(Document)的,文档是所有可搜索数据的最小单 ...
- ☕【Java技术指南】「并发原理专题」AQS的技术体系之CLH、MCS锁的原理及实现
背景 SMP(Symmetric Multi-Processor) 对称多处理器结构,它是相对非对称多处理技术而言的.应用十分广泛的并行技术. 在这种架构中,一台计算机由多个CPU组成,并共享内存和其 ...
- Sqli-Labs less8-10
less-8 前置基础知识: 前几关我们用到了布尔盲注的办法,还有一种盲注就是时间盲注,不仅可以用于有回显的盲注,还能用于没有回显的盲注 函数:sleep(1):等待1秒之后再返回页面做出反应 IF( ...
- HttpClient调用doGet、doPost、JSON传参及获得返回值
调用 doPost:map传参 Map<String,Object> map = new HashMap<>(); map.put("test"," ...
- 微信小程序中h5跳转到登录页面,登陆成功返回携带参数,h5刷新
公司的一个小程序,要做一个活动,需要判断登录状态. 思路:h5跳转到登录页面,登陆成功携带token自动返回. 本来以为是个非常简单的功能,没想到..... 发帖记录一下 1.登录页面 用getCur ...
- mysql删除大表更快的办法
实现:巧用LINK(硬链接),原理:linux文件系统中硬链接相当于文件的入口,记录着ionde的信息.一个文件存在多个硬连接时,删除一个硬链接不会真正的删除ionde(存储文件的数据) # 创建硬链 ...
- shell 函数返回值与字典
shell的函数只能返回整数值,如果想让函数返回字符串可以在函数调用处为变量赋值. # 定义函数function test() { name=$1 echo "123213" } ...
- flutter添加启动图及设置启动时间
首先贴个官方的设置方法,看这里:https://flutterchina.club/assets-and-images/#%E6%9B%B4%E6%96%B0%E5%90%AF%E5%8A%A8%E9 ...