【DRP】树形结构操作之递归删除

如图所示呈现了一颗树形结构。本文从删除树形结构的任意结点出发,提供了一种解决思路

图中,不包含其它结点的是叶子结点。包含其他结点的是父结点,即不是叶子结点。

一 本文的知识点:

(1)递归调用:

因为待删除的结点的层次是不确定的,如果是叶子结点则可以直接获取id直接删除,如:北京中医医院、华北区。如果待删除的结点是父结点,则需要继续向下查询,依次遍历出其子结点,从下往上依次删除,如‘华北区’。因此我们使用递归调用。

(2)保证事务的原子性

假设待删除的结点是‘华北区’,则相当于删除了3条信息(华北区、北京市、北京中医医院),通常认为同时删除多条数据,是具有原子性的。删除100条信息,相当于一个事务。要么一起删除,要么都不删除。

事务控制,表现为两方面:一个是递归删除过程中采用一个Connection(同时防止递归中循环调用导致Connection效率不高);另一个是设置手动提交。

(3)异常是throws ?还是catch?

主方法delClientOrRegion为public 采用try…catch……finally打印堆栈中的错误信息;主方法中调用的方法,如:recursionDelNode、getChildren、modifyIsLeafField,均为private仅供方法内部调用,同时采用throws
SQLException抛出异常,而不是catch捕捉异常。

这是因为假设getChildren内部catch了错误,打印了堆栈信息,那么上面一层不知道它出错的话,因此就不会事务回滚。因此建议内部抛出异常throws
SQLException,外部使用捕获异常try catch

(4)树的几种设计方式

1)不带冗余字段,id主键,pid父结点主键

2)带冗余字段,id、pid、isleaf是否为叶子、childrencount孩子结点的数目

3)采用固定的字符串00010010001

00所有分销商

01华北区

001北京市

0001北京中医医院

(本数据库设计采用:id主键、pid父结点主键、is_leaf是否为叶子结点)

(5)业务逻辑解析,假设待删除结点为‘北京市’

1)保存父结点对象。即:获取‘北京市’的父结点‘华北区’的id。

2)递归删除树结点。即:删除‘北京市’同时还要删除其孩子结点‘北京中医医院’。删除的顺序也是有要求的,先删除孩子,后删除父亲。即先删除‘北京中医医院’,后删除‘北京市’,这一点在递归代码中有所体现。

3)如果父结点下没有子结点,则修改为叶子结点。即:如果‘华北区’没有孩子,则需要设置‘华北区’为叶子结点。

二 部分代码如下:

package com.bjpowernode.drp.basedata.manager;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.bjpowernode.drp.basedata.domain.AimClient;
import com.bjpowernode.drp.basedata.domain.Client;
import com.bjpowernode.drp.util.Constants;
import com.bjpowernode.drp.util.DbUtil;
import com.bjpowernode.drp.util.IdGenerator;
import com.bjpowernode.drp.util.PageModel;
import com.bjpowernode.drp.util.datadict.domain.ClientLevel;

/**
 * 采用单例实现
 * @author Administrator
 *
 */
public class ClientManager {

	private static ClientManager instance = new ClientManager();

	private ClientManager() {}

	public static ClientManager getInstance() {
		return instance;
	}

		/**
	 * 删除分销商或区域
	 * @param id
	 */
	public void delClientOrRegion(int id) {
		Connection conn = null;
		try {
			conn = DbUtil.getConnection();
			//手动控制事务的开启
			DbUtil.beginTransaction(conn);

			//保存父节点对象
			Client currentNode = findClientOrRegionById(id);

			//递归删除树节点
			recursionDelNode(conn, id);

			//如果父节点下没有子节点
			if (getChildren(conn, currentNode.getPid()) == 0) {
				//修改为叶子
				modifyIsLeafField(conn, currentNode.getPid(), Constants.YES);
			}
			//提交事务
			DbUtil.commitTransaction(conn);
		}catch(Exception e) {
			//如果出错则打印堆栈信息
			e.printStackTrace();
			//事务回滚
			DbUtil.rollbackTransaction(conn);
		}finally {
			//事务重置
			DbUtil.resetConnection(conn);
			//关闭数据库连接
			DbUtil.close(conn);
		}
	}
	/**
	 * 递归删除
	 * @param conn
	 * @param id
	 */
	private void recursionDelNode(Connection conn, int id)
	throws SQLException {
		//根据
		String sql = "select * from t_client where pid=?";
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			conn = DbUtil.getConnection();
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				//如果是不叶子节点,则递归调用
				if (Constants.NO.equals(rs.getString("is_leaf"))) {
					recursionDelNode(conn, rs.getInt("id"));
				}
				//如果是叶子节点,则直接删除
				delNode(conn, rs.getInt("id"));
			}
			//删除自身
			delNode(conn, id);
		}finally {
			DbUtil.close(rs);
			DbUtil.close(pstmt);
		}
	}
		/**
	 * 取得指定节点的孩子数目
	 * @param conn
	 * @param id
	 * @return
	 */
	private int getChildren(Connection conn, int id)
	throws SQLException {
		//sql语句通过孩子pid的个数,判断是否为叶子节点
		String sql = "select count(*) as c from t_client where pid=?";
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		int count = 0;
		try {
			conn = DbUtil.getConnection();
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, id);
			rs = pstmt.executeQuery();
			rs.next();
			count = rs.getInt("c");
		}finally {
			DbUtil.close(rs);
			DbUtil.close(pstmt);
		}
		return count;
	}
		/**
	 * 修改isLeaf字段
	 * @param conn
	 * @param id
	 * @param leaf Y/N
	 */
	private void modifyIsLeafField(Connection conn, int id, String leaf)
	throws SQLException {
		//如果待删除的节点的父节点没有其他孩子节点,则设置为叶子状态
		String sql = "update t_client set is_leaf=? where id=?";
		PreparedStatement pstmt = null;
		try {
			conn = DbUtil.getConnection();
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, leaf);
			pstmt.setInt(2, id);
			pstmt.executeUpdate();
		} finally {
			DbUtil.close(pstmt);
		}
	}
}

【DRP】树形结构操作之递归删除

时间: 2024-12-11 11:04:32

【DRP】树形结构操作之递归删除的相关文章

java、js中实现无限层级的树形结构(类似递归)

js中: var zNodes=[{id:0,pId:-1,name:"Aaaa"},    {id:1,pId:0,name:"A"},    {id:11,pId:1,name:"A1"},    {id:12,pId:1,name:"A2"},    {id:13,pId:1,name:"A3"},    {id:2,pId:0,name:"B"},    {id:21,pId:2

js中实现无限层级的树形结构(类似递归)

这个是后台管理的动态创建的菜单,比较难,不过,仔细揣摩还是比较简单的,所以,直接上代码. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="div"></div&g

MVC3+EF4.1学习系列(十)----MVC+EF处理树形结构

通过前几篇文章 我们处理了 一对一, 一对多,多对多关系 很好的发挥了ORM框架的做用 但是 少说了一种 树形结构的处理, 而这种树形关系 我们也经常遇到,常见的N级类别的处理, 以及经常有数据与类别挂钩.今天主要写下EF处理树形结构以及 MVC如何展示树形结构. 前面几篇的例子 一直用的是一个例子,内容是连贯的.这篇是完全单独的~ 先来说下工作中会遇到的常见场景 针对这几个场景来处理~ 1.类别 a.类别可以有无限级别 b.类别的最末端 不确定是第几级 某个节点 可以到二级 其他的节点 有可能

java工程积累——树形结构的操作

最近一直被树形结构整的很头大,又是递归,又是循环,但是,好在我们在经历了千辛万苦后,终于弄出来了,其实就是组织机构的常规操作,有些是我们过度设计,有些是我们想错了,而对数的逻辑读取,我们就属于想错了的类型,今天拿出来和大家分享,主要是树形结构在数据库的读取问题! 原始: 在最开始,我们对树的查询,肯定是从最简单的select开始,我们现在回顾一下: 定义: 表名:tb_tree 字段:id(主键),title(标题),parentId(父节点id) 举例: 1.查找树中的所有顶级父节点(辈份最长

javaproject积累——树形结构的操作

近期一直被树形结构整的非常头大,又是递归.又是循环.可是,好在我们在经历了千辛万苦后.最终弄出来了.事实上就是组织机构的常规操作,有些是我们过度设计.有些是我们想错了.而对数的逻辑读取,我们就属于想错了的类型.今天拿出来和大家分享.主要是树形结构在数据库的读取问题! 原始: 在最開始.我们对树的查询,肯定是从最简单的select開始.我们如今回想一下: 定义: 表名:tb_tree 字段:id(主键),title(标题),parentId(父节点id) 举例: 1.查找树中的全部顶级父节点(辈份

递归、嵌套for循环、map集合方式实现树形结构菜单列表查询

有时候, 我们需要用到菜单列表,但是怎么样去实现一个菜单列表的编写呢,这是一重要的问题. 比如我们需要编写一个树形结构的菜单,那么我们可以使用JQuery的zTree插件:http://www.treejs.cn/v3/main.php#_zTreeInfo 例如现在需要编写一个这样的菜单列表.那么就可以使用JQuery的zTree插件. 先看一下数据库表结构. CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `

「SQL归纳」树形结构表的存储与查询功能的实现——通过路径方法(非递归)

一.树形结构例子分析: 以360问答页面为例:http://wenda.so.com/c/ 我们通过观察URL,可以明确该页面的数据以树形结构存储,下面三块模块分别为: ①根节点 ②根节点的第一层子节点 ③为左侧所选择节点的下一层子节点 (图1) 该例简化的树形结构图如下: (图2) 我们不难发现,每当点击图1红框内的类别时,页面主体问题部分会显示该类别节点下所有子节点的问题.因此,需要实现查询出某节点所有子节点的功能. 二.表的存储: 需要存储两张表: 1.类别表 create table [

树形结构根据某节点查询本节点及下属所有子节点的递归实现

数据表中CompanyId,ParentCompany,有层级关系,树形结构根据某节点查询本节点及下属所有子节点的递归实现如下: public string ids = ""; /// <summary> /// 根据CompanyId 查找到子单位id /// </summary> /// <param name="companyId"></param> /// <returns></returns

递归生成树形结构

原文地址:https://blog.csdn.net/q13965211/article/details/80772544 节点树树形结构 Tree 结构 import java.util.List; /** * @Author fanwei * @date 2018-6-13 17:04 */ public class TreeNode { private Integer key; private String title; private Integer parentId; private