树形结构——基本原理

说明

    首先需要声明,本篇博客是实现单实体的最基本的树形结构效果,不涉及第三方组件内容。万变不离其宗,无论是通过哪种方式,其基本的思路还是一致,希望通过本篇博客的讲解,能对初次研究树形结构的朋友有所启发。后面是实现界面显示公司规模结构效果的例子。


实现

    数据库对应表结构:


    为方便程序实现,我们将区域和公司信息,统一放入公司信息表。  

    再来看VJSP页面呈现:

<%@ page language="java" contentType="text/html; charset=GB18030"
	pageEncoding="GB18030"%>
<%@ page import="com.tgb.basetree.manager.*" %>

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
		<link rel="stylesheet" href="../style/drp.css">
		<title>组织结构</title>
	</head>
	<body class="body1">
		<table>
			<tr>
				<td valign="top" nowrap="nowrap">
					<%=CompanyTreeReader.getInstance().getClientTreeHTMLString() %>
				</td>
			</tr>
		</table>
	</body>

</html>
    CompanyTreeReader类中调用了getClientTreeHTMLString方法,实现如下:

//定义返回给客户端的HTML字符串
private StringBuffer sbTreeHTML = new StringBuffer();

/**
 * 方法入口(为递归调用提供统一的数据库连接)
 * @return 返回HTML字符串
 */
public String getClientTreeHTMLString(){
	//递归调用时使用同一连接字符串,避免多次与数据库进行连接
	Connection conn = null;
	try{
		conn = DbUtil.getConnection();
		//调用递归实现读取的方法,第二个参数设置为0,则表示从根节点读取
		readCompanyTree(conn, 0, 0);
	}catch(Exception e){
		e.printStackTrace();
	}finally{
		//关闭数据库连接
		DbUtil.close(conn);
	}
	return sbTreeHTML.toString();
}

    然后,我们从最简单的查询数据库开始实现拼接界面要显示的 HTML 树形结构页面的效果

          1.首先我们实现最简单的查询,可以显示到浏览器中即可

/**
 * 递归读取公司表的数据信息
 * 
 * 第一步: 以最简单的方式读取分销商树形结构,可以显示到浏览器中即可
 * @param conn
 * @param id
 * @param leavel  控制层次
 */
private void readCompanyTree(Connection conn, int id, int leavel)
throws SQLException{
	
	PreparedStatement pstmt = null;
	ResultSet result = null;
	
	//查询字符串
	String strSql = "Select id, pid, name, company_id, contact_tel, address, is_company, zip_code, is_leaf from t_company where pid=?";
	try{
		pstmt = conn.prepareStatement(strSql);
		
		//查询语句参数赋值
		pstmt.setInt(1, id);
		result = pstmt.executeQuery();
		
		//遍历查询结果集
		while(result.next()){
			//拼接HTML字符串
			sbTreeHTML.append(result.getString("name"));
			sbTreeHTML.append("<br />\n");
			
			//不是叶子节点,递归调用执行
			if("N".equals(result.getString("is_leaf"))){
				readCompanyTree(conn,Integer.parseInt(result.getString("id")),leavel);
			}
		}
	}finally{
		//执行结束关闭资源
		DbUtil.close(result);
		DbUtil.close(pstmt);
	}
}
    界面显示效果:
    

     2.实现 分层显示效果

/**
 * 递归读取公司表的数据信息
 * 
 * 第二步: 加入层次感,并在叶子节点前加“-”号,非叶子节点前加“+”
 * @param conn 数据库连接字符串
 * @param id 根据pid查询的关键字值
 * @param leavel  控制层次
 */
private void readCompanyTree(Connection conn, int id, int level)
throws SQLException{
	
	PreparedStatement pstmt = null;
	ResultSet result = null;
	
	//查询字符串
	String strSql = "Select id, pid, name, company_id, contact_tel, address, is_company, zip_code, is_leaf from t_company where pid=?";
	try{
		pstmt = conn.prepareStatement(strSql);
		
		//查询语句参数赋值
		pstmt.setInt(1, id);
		result = pstmt.executeQuery();
		//遍历查询结果集
		while(result.next()){
			//获取level值,得出空格个数
			for(int i=0; i < level; i++){
				sbTreeHTML.append("  ");
			}
			
			if("N".equals(result.getString("is_leaf"))){
				sbTreeHTML.append("+")
					.append(result.getString("name"))
					.append("<br />\n");
				//不是叶子节点,递归调用执行,level层次参数同步递增
				readCompanyTree(conn,result.getInt("id"),level+1);
			}else{
				sbTreeHTML.append("-")
					.append(result.getString("name"))
					.append("<br />\n");
			}
		}
	}finally{
		//执行结束关闭资源
		DbUtil.close(result);
		DbUtil.close(pstmt);
	}
}

    界面显示:


        3.JS控制显示隐藏节点效果

    再开始继续完善之前,我们首先看不存在数据交换的树形结构的纯HTML方式

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=GB18030">

	<script language="JavaScript">
		//根据传入id值,确定显示/隐藏嵌套的div的ID值,进而切换显示/隐藏效果
		function display(id) {
			 eval("var div=div"+id);
			 eval("var img=img"+id);
			 eval("var im=im"+id);
			 div.style.display=div.style.display=="block"?"none":"block";
			 img.src=div.style.display=="block"?"../images/minus.gif":"../images/plus.gif";
			 im.src=div.style.display=="block"?"../images/openfold.gif":"../images/closedfold.gif";
			 img.alt=div.style.display=="block"?"关闭":"展开";
		}

	</script>
	</head>
	<body class="body1">
		<table>
			<tr>
				<td valign="top" nowrap="nowrap">
					<div>
						<img alt="展开" style="cursor:hand;" onClick="display('1');"
							id="img1" src="../images/plus.gif">
						<img id="im1" src="../images/closedfold.gif">
						<a href="#" target="clientDispAreaFrame">所有分销商</a>
						<div style="display:none;" id="div1">
							<div>
								<img src="../images/white.gif">
								<img alt="展开" style="cursor:hand;" onClick="display('2');"
									id="img2" src="../images/plus.gif">
								<img id="im2" src="../images/closedfold.gif">
								<a href="#" target="clientDispAreaFrame">华北区</a>
								<div style="display:none;" id="div2">
									<div>
										<img src="../images/white.gif">
										<img src="../images/white.gif">
										<img alt="展开" style="cursor:hand;" onClick="display('3');"
											id="img3" src="../images/plus.gif">
										<img id="im3" src="../images/closedfold.gif">
										<a href="#" target="clientDispAreaFrame">北京市</a>
										<div style="display:none;" id="div3">
											<div>
												<img src="../images/white.gif">
												<img src="../images/white.gif">
												<img src="../images/white.gif">
												<img src="../images/minus.gif">
												<img src="../images/openfold.gif">
												<a href="#" target="clientDispAreaFrame">北京总公司</a>
											</div>
										</div>
									</div>
								</div>
							</div>
							<div>
								<img src="../images/white.gif">
								<img src="../images/minus.gif">
								<img src="../images/openfold.gif">
								<a href="#" target="clientDispAreaFrame">东北区</a>
							</div>
							<div>
								<img src="../images/white.gif">
								<img src="../images/minus.gif">
								<img src="../images/openfold.gif">
								<a href="#" target="clientDispAreaFrame">华南区</a>
							</div>
						</div>
					</div>
				</td>
			</tr>
		</table>
	</body>
</html>

    根据上面的模版,我们就可以在后台继续拼成我们最终的 HTML 代码:

/**
 * 递归读取公司信息树
 * 
 * 第三步:	采用DIV生成树形结构
 * @param conn
 * @param id
 * @param leavel  控制层次
 */
private void readCompanyTree(Connection conn, int id, int level)
throws SQLException{
	PreparedStatement pstmt = null;
	ResultSet result = null;
	String strSql = "Select id, pid, name, company_id, contact_tel, address, is_company, zip_code, is_leaf from t_company where pid=?";
	try{
		pstmt = conn.prepareStatement(strSql);
		pstmt.setInt(1, id);
		result = pstmt.executeQuery();
		while(result.next()){
			sbTreeHTML.append("<div>")
				.append("\n");
			for(int i=0; i < level; i++){
				sbTreeHTML.append("<img src=\"images/white.gif\">")
					.append("\n");
			}
			//if("N".equals(result.getString("is_leaf"))){
			if("N".equals(result.getString("is_leaf"))){
				//拼接字符串过程,对应上面HTML模版中的内容,可进行简要粘贴、简要修改
				sbTreeHTML.append("<img alt=\"展开\" style=\"cursor:hand;\" onClick=\"display('"+ result.getInt("id") +"');\" id=\"img"+ result.getInt("id") +"\" src=\"images/plus.gif\">")
					.append("\n")
					.append("<img id=\"im"+ result.getString("id") +"\" src=\"images/closedfold.gif\">")
					.append("\n")
					.append("<a href=\"#\">" + result.getString("name") + "</a>")
					.append("\n")
					.append("<div style=\"display:none;\" id=\"div"+ result.getInt("id") +"\">");
				readCompanyTree(conn,result.getInt("id"),level+1);
				sbTreeHTML.append("</div>");
			}else{
				sbTreeHTML.append("<img src=\"images/minus.gif\">")
					.append("\n")
					.append("<img src=\"images/openfold.gif\">")
					.append("\n");
					if("N".equals(result.getString("is_company"))){
						sbTreeHTML.append("<a href=\"#\" >"+ result.getString("name") +"</a>");
					}else{
						sbTreeHTML.append("<a href=\"#\" >"+ result.getString("name") +"</a>");
					}
			}
			sbTreeHTML.append("</div>");
		}
	}finally{
		DbUtil.close(result);
		DbUtil.close(pstmt);
	}
}

    最后实现效果:


总结

    最后形成的树形结构,是通过对JS代码控制div显示/隐藏的简单应用实现的,整个实现过程的关键点在于:方法的递归调用,后台拼接HTML代码串以及JS的灵活使用。

    在实际项目中,我们大部分是通过第三方工具来实现树形结构,例如ZTree等,在明白基本原理的层面再去应用,相信会更加清楚明了一些。本篇博客仅仅是从最最基本的实现方式入手,简单阐述并实现树形结构的实例。其中还有很多不足住处,例如多实体的级联操作,冗余字段的影响等内容,在此也仅仅是个入门学习而已。

    树形结构作为一项系统中经常用到的一项技术,我们不仅仅需要进行读取、界面显示的效果,对其中节点的CRUD操作更是常见,写至此时,也联想到了设计模式中的组合模式,正是灵活应对树形节点增删改的好方法,后面将会进行更进一步的探讨。


评论 69
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值