在项目中,大家可能会遇到这样一个问题,就是当你操作那些具有上下级的树的表的时候,如果是单纯的父子级关系,可能不会碰见这个问题,但是如果这个看起来具有树形结构的表形成闭环的时候,问题就来了,我遇到的结果就是,一旦碰到这样的情况,就是页面一直卡在那里,对应着后台就是,要么程序死循环,要么数据库死循环,直到系统卡死崩溃。

这个问题很是头疼,在网上找了不少资料没有发现真正能够解决我的问题的,究其原因,还是这样的树形结构用的少,平常大家使用的多的还是那种无限层级的,也就是父子级可以无限下去的,这里就来看看我这个问题和解决办法吧。

首先数据库里面有一张表,其中有两个很关键的字段,targetIden和srcIden,这两个字段用于标记理论上可作为父子级的一条关系数据,可以理解成targetIden是父节点数据,srcIden是子节点数据,如图所示,


在这个表中我创建了几条测试数据,相信大家可以看出来,已经形成了一条联试结构的数据,也就是两两之间存在某种隐含的父子级关系,现在假如来了一个需求,需要创建一条尾号为 …55 和 …411的数据,也就是插入一条这样的数据,可以吗?

这个很简单,这需要执行一下插入即可,没问题,可以插入成功,问题就在这时候产生了,当这条数据插入成功后,我们在进行查询,比如以411为条件查询所有的关系数据,这时候悲催的问题就产生了,不管是用程序递归还是数据库的递归来查询,都会造成死循环,其实两者查询本质一样,都是递归,但是结果都是死循环儿查不出结果来,因为从数据结构上来说,形成了一条闭环的数据链路;

解决的思路无非有两种,
1、通过其他的方式查询出这个链路的数据【这个我想了不少办法,还是没有很好的解决方式】
2、插入数据的时候将这个问题规避掉;

对比了一下实现成本,第二张方式相对简单一点,下面就用第二种方式实现以下;

既然选择了第二种思路,我们先思考一下,在插入数据的时候,我们该怎么避免这个死循环的结构数据产生呢?

还是以上面的用555和411为例,我们插入411和555的关系之前,对于555这条数据,是不是需要把插入之前555的整个上下链路的所有数据查出来,查出来以后呢?只需要判断一下对应的411这条数据是否存在于555的前置链路或者后置链路上不久可以了吗

解决方法就在此,插入之前,我们把555之前的链路数据全部查询出来,555之后的数据全部查出来,然后基本上就出来了,下面看具体代码,主要就是一个方法类,

/*** 判断是否存在死循环节点* @param params* @return*/public boolean isDeadNodes(Map params){String targetIden = params.get("targetIden").toString();String srcIdne = params.get("srcIden").toString();List<String> exists = this.baseDao.queryForListBySql("fieldanalysisMapping.existTreesOld",params);if(exists != null && exists.size() >0){//存在关联的数据了,不允许存在return true;}//如果父节点为空,可以直接进行插入数据//1、查找父节点是否已经存在了?List<String> targetIds = this.baseDao.listHql("select id from MdRelation where targetIden =  '"+targetIden+"' or srcIden = '"+targetIden+"'");List<String> beforesLeft = new ArrayList<>();List<String> aftersRight = new ArrayList<>();if(targetIds == null || targetIds.size() ==0){// 父节点根本不存在return false;}else {//父节点存在,查找父节点的所有前置节点   ===  影响关系数据Map beforeMap = new HashMap();beforeMap.put("fieldIden",targetIden);List<Map<String,String>> beforList =this.baseDao.queryForListBySql("fieldanalysisMapping.getFieldsEffectsForFunction", beforeMap);if(beforList != null && beforList.size() >0){for(Map one:beforList){beforesLeft.add(one.get("TARGET_IDEN").toString());beforesLeft.add(one.get("SRC_IDEN").toString());}}//再找到后置节点数据Map afterMap = new HashMap();afterMap.put("fieldIden",targetIden);List<Map<String,String>> aftersList =this.baseDao.queryForListBySql("fieldanalysisMapping.getFieldsBloodsForFunction", beforeMap);if(aftersList != null && aftersList.size() >0){for(Map one:aftersList){aftersRight.add(one.get("TARGET_IDEN").toString());aftersRight.add(one.get("SRC_IDEN").toString());}}}List<String> srcIds = this.baseDao.listHql("select id from MdRelation where targetIden =  '"+srcIdne+"' or srcIden = '"+srcIdne+"'");//如果子节点存在,if(srcIds == null || srcIds.size() ==0){    //子节点根本不存在return false;}else{//子节点存在了,判断是否存在于父节点的前置或者后置节点上,影响关系是后置,血缘关系是前置if(beforesLeft == null || beforesLeft.size() ==0){//如果前置节点为空if(aftersRight != null && aftersRight.size() >0){//后置节点存在if(aftersRight.contains(srcIdne)){return true;}else{return true;}}}else{      //如果前置节点不为空if(aftersRight==null ||aftersRight.size()==0){ //如果后置节点为空if(beforesLeft.contains(srcIdne)){return true;}else{return false;}}else{  //前后节点都存在if(aftersRight.contains(srcIdne) && !beforesLeft.contains(srcIdne)){return false;}if(!aftersRight.contains(srcIdne) && beforesLeft.contains(srcIdne)){return true;}}}}return false;}

方法中对应的几个查询语句的mybatis方法也列举在下面,这里采用了数据库递归的方式,即采用了with - as函数的写法,分别查询555这条数据的前置和后置节点,

后置节点数据,

<select id="getFieldsBloodsForFunction" parameterType="java.util.Map" resultType="java.util.Map">WITH PPL (ID,TARGET_IDEN,SRC_IDEN,RULE_TYPE,RULE_DESC) AS(SELECT ID,TARGET_IDEN,SRC_IDEN ,RULE_TYPE,RULE_DESC FROM MD_RELATION WHERE TARGET_IDEN = #{fieldIden}UNION ALLSELECT child.ID,child.TARGET_IDEN,child.SRC_IDEN ,child.RULE_TYPE, child.RULE_DESC FROM PPL parent,MD_RELATION child WHERE child.TARGET_IDEN = parent.SRC_IDEN)SELECT * FROM PPL</select>

前置节点数据:

 <select id="getFieldsEffectsForFunction" parameterType="java.util.Map" resultType="java.util.Map">WITH PPL (ID,TARGET_IDEN,SRC_IDEN,RULE_TYPE,RULE_DESC) AS(SELECT ID,TARGET_IDEN,SRC_IDEN ,RULE_TYPE,RULE_DESC FROM MD_RELATION WHERE SRC_IDEN = #{fieldIden}UNION ALLSELECT child.ID,child.TARGET_IDEN,child.SRC_IDEN ,child.RULE_TYPE, child.RULE_DESC FROM PPL parent,MD_RELATION child WHERE child.SRC_IDEN = parent.TARGET_IDEN)SELECT * FROM PPL</select>

因为我们的目标数据是555和411的关系,所有555的前置和后置的关系数据结构就显示成如上的结构,然后我们调用一下接口看看效果,可以看到我们已经将这条数据拦截到了,同样,我们再测试一个,


我们使用96和38这条数据测试,因为他们也是可能造成死循环的树,

可以看到也拦截到这对可能造成死循环的数据了,

通过上面的方式基本上达到了预期的效果,希望对看到的小伙伴有所有帮助!

解决 mysql 树形结构插入数据查询死循环问题相关推荐

  1. 解决MySQL删除和插入数据很慢的问题

    推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...

  2. mybatis mysql 树形结构_Mybatis查询树形结构数据

    数据表的设计 创建数据表 对于树形结构的数据库设计通常是基于继承关系设计的,也就是通过父ID关联来实现的. 一张树形结构的数据表基本的三个字段:id(自己).name(名称)和parentid(父类编 ...

  3. 玩转Redis-Lua脚本入门到实战-树形结构存储及查询

      <玩转Redis>系列文章 by zxiaofan主要讲述Redis的基础及中高级应用,穿插企业实战案例.本文是<玩转Redis>系列第[16]篇,最新系列文章请前往 公众 ...

  4. 【C 语言】文件操作 ( 学生管理系统 | 插入数据 | 查询数据 | 删除数据 )

    文章目录 一.学生管理系统 1.插入数据 2.查询数据 3.删除数据 二.完整代码 一.学生管理系统 实现一个简易学生管理系统 , 验证文件操作 ; 1.插入数据 从命令行接收数据 , 放入结构体成员 ...

  5. 树形结构的数据存储和数据库表设计

    ID int 主键 OBJECTNAME nvarchar(50) 对象名称 OBJECTTYPE  nvarchar(1)  对象级别 PARENTID int 对象父ID 其实对象级别这个字段可以 ...

  6. php怎么插入数据,利用PHP怎么向MySQL数据库中插入数据

    利用PHP怎么向MySQL数据库中插入数据 发布时间:2020-12-11 16:41:47 来源:亿速云 阅读:89 作者:Leah 这篇文章给大家介绍利用PHP怎么向MySQL数据库中插入数据,内 ...

  7. 【MySQL】5.0 数据查询

    数据查询 基本查询 条件查询 连接查询 内连接查询(INNER JOIN) 外连接查询(OUTER JOIN) 复合连接查询 高级应用 Limit Union GROPE BY ORDER BY DI ...

  8. mysql常见问题处理-插入数据error code:1206

    mysql常见问题处理-插入数据error code:1206 mysql  error code:1206 the total number of locks exceeds the lock ta ...

  9. 《1---关于解决MySQL在控制台插入中文乱码问题》

    <1---关于解决MySQL在控制台插入中文乱码问题> 参考文章: (1)<1---关于解决MySQL在控制台插入中文乱码问题> (2)https://www.cnblogs. ...

最新文章

  1. 从命令行修改你的Mac DNS(主要在在线恢复mac系统不能上网时候使用)
  2. UniDAC使用教程(二):数据更新
  3. qml基础学习 基础概念
  4. antd 中table上加不同字体颜色_字体渲染系统!微软终于决定优化Win10字体模糊问题...
  5. 10一个应用阻止关机贴吧_手机该不该每天关机一次?看完才知道这么多年白用了...
  6. HTML placeholder
  7. 22.案例实战:把springboot的接口,自动生成接口文档
  8. 将执行文件转化为bat批处理文件的工具(批处理文件方式提供)
  9. from django.core.context_processors import crsf报错
  10. 云计算时代的数据库研究
  11. 四波混频 matlab,四波混频(four-wave mixing)
  12. 晒晒我的“无法操作”的“发财计划”
  13. 成为一名程序员的开始
  14. 尊贤、谦虚、谨慎、交友、有恒、微渐、慎始终、因果
  15. java中talent-aio_通讯框架:talent-aio实例
  16. 数字图像处理学习笔记(四)点处理:灰度值反转、对数变换、伽马变换
  17. windows Phone 8 (1)
  18. ansa打开catia文件_ANSA软件介绍.doc
  19. php智能搜索,WordPress中文分词与智能搜索
  20. 红队笔记之渗透测试流程以及各环节技术纲要

热门文章

  1. Equals,ReferenceEquals,==的区别
  2. HttpApplication的认识与加深理解
  3. Windows Mobile 7(Photon) 梦幻之旅系列-前言
  4. 时间同步服务 chrony
  5. 从Maven远程存储库下载
  6. Hadoop学习之路(十三)MapReduce的初识
  7. 求1到N的全排列 (转载)
  8. centos常用网络管理命令
  9. H3C 防火墙无法ping通端口
  10. hdu 3746 kmp求循环节 下标从1开始