项目场景:
将一个树上的节点插入到数据库,节点之间有父子关系,每个节点有一个id和pId,父子关系表述为:父节点的id为子节点的pId值。在上一个日志中,所有节点的值已经全部打包完传给了后台。现在执行插入操作。
难点:
举个例,A是根节点,他有一个treeid,姑且叫Aid,他的父id为null。插入数据库后会生成一个新的id,姑且叫NewAid,
以Aid作为父id的有五个儿子BCDEF,将继续插入数据库,此时他们以新生成的NewAid作为pid这个字段值插入数据库,生成了新的Bid,Cid,Did,Eid,Fid.依此类推BCDEF下面有B1,B2;C1,C2,C3;D1,D2,D3,D4>>>>>>>继续插入
分析一下,其实就是说在前端时,每个节点都有个treeid和pid,传到后台是为了找他们的子节点,
当每个节点生成了新的id后,就将这个id作为子节点的pid继续插入子节点,又生成一批新的id作为子节点的子节点的pid
所以,难点就是递归怎么写,因为我以前遇到的少,所以卡壳了一天才完成,fastJson上篇已经说明
直接上代码说明,无关代码可忽略
public void insertCatalog(){ HttpServletRequest req = ServletActionContext.getRequest(); String datalist = req.getParameter("datalist"); JSONArray ja = JSON.parseArray(datalist);//解析传过来的json串 JSONObject rootJO = ja.getJSONObject(0);//入口ja的第一个对象约定是根节点,先插入 String newid = add2DB(rootJO);//插入后返回id String oldid = rootJO.getString("id");//这是前台传过来的id ja.remove(0);//插入一条就移走一条,减少后面递归的量 insert(ja, newid,oldid);//递归插入的入口 }
public void insert(JSONArray ja,String newid,String oldid) { if (newid==null || "".equals(newid) ) return; //如果返回的newid不对劲,终止 if (ja.isEmpty()) return; //递归插入的出口 for (int i=0; i<ja.size(); i++) { JSONObject jo = ja.getJSONObject(i); if ( oldid.equals( jo.get("pId") ) ) { //根据每个节点的id寻找他的儿子们 jo.remove("parentID"); //如果找到了,换掉parentID这个属性的值 jo.put("parentID", newid); //给他赋的值就是每次生成的newid String newCreateId = add2DB(jo); //插入,返回新的id String currentId = jo.getString("id"); //这是当前被插入的节点的id,作为下次寻找儿子们用 ja.remove(i); //这里每插入一条,移除 insert(ja, newCreateId,currentId); //递归方法 } } }
add2DB这个方法就不记录了,就是茶如数据库,返回插入后的新id的方法
到此,这个递归就完成了,测试插入成功,写完了还是觉得意犹未尽~~~~~~~~~~~~~~~~~~~~~~~~~
**********************************************************************************************************************************************
以上递归方法测试,会跳过偶数下标的数据,现在没有找到问题出在哪儿,最大的可能是,for循环内部又使用了递归方法
查阅过相关技术大牛的文章,有人对for循环内部再递归的情形做过研究,我自己觉得这样设计(有时候不得不)对程序逻辑流需要一定的
想象力。再者需要了解一个东西。递归只会往深处调用,不存在什么跳出for循环外调用自己。
首先,java的堆存对象,栈存基本数据类型和对象引用地址这个是基本的,也就是说,每一次递归方法调用都会重新产生很多变量值,如果再加上for循环,那么栈里面的变量增加会很快,栈中的变量一般只会在程序结束的时候才会被清空,所以当循环很多,或者递归很深的时候,jvm的栈可能会被塞爆。这个仅做一个了解。
我要说的是,递归的时候,不用担心递归之前的变量被覆盖或清空了,因为每次调用方法,栈都会重新给这个方法(要产生变量)分配属于该方法的内存,也就是分配一块儿地方,所以递归只会往更深的方向执行方法,打个比方,A方法在内部递归调用自己,好比A克隆了A1,递归的时候调的A1,然后继续克隆继续调用,体现在栈里面就是不断分配空间给这些A1,A2,A3、、、、、、
这个递归的方法我改了一下,
public void insert(JSONArray ja,String newid,String oldid) { if (newid==null||"".equals(newid)) return; //递归插入的出口 if(ja.isEmpty())return; List<JSONObject> joList = search(ja,oldid);//先搜索儿子们,存到joList 中 ja.removeAll(joList);//搜索完了全部移除 for(int i=0;i<joList.size();i++){ JSONObject jo = joList.get(i); jo.remove("parentID"); jo.put("parentID", newid); String newCreateId = add2DB(jo); String currentId = jo.getString("id"); insert(ja,newCreateId,currentId); } }
public List<JSONObject> search(JSONArray ja,String id){ if(ja.isEmpty())return null; List<JSONObject> joList = new ArrayList<JSONObject>(); for(int i=0;i<ja.size();i++){ JSONObject jo = ja.getJSONObject(i); if(id.equals(jo.get("pId"))){ joList.add(jo); } } return joList; }
然后由根节点原来的id在数据包ja里,调search方法返回他的所有子节点装进list,根节点的子节点们移除
遍历list循环插入,每插入一条产生新id,再调用搜索方法搜索他的儿子们,移除,插入,依次往深处递归执行
直到“第一条”的id下的所有儿子们全部插入完成,开始插入“第二条”
第二条的模式和第一条一样,也是插入,产生id,找儿子,插入儿子,儿子找孙子继续插入
直到第一批的节点全部插入完成,所有节点就全部插入完成