说明
首先须要声明,本篇博客是实现单实体的最主要的树形结构效果。不涉及第三方组件内容。万变不离其宗。不管是通过哪种方式,其主要的思路还是一致,希望通过本篇博客的解说,能对初次研究树形结构的朋友有所启示。后面是实现界面显示公司规模结构效果的样例。
实现
数据库相应表结构:
为方便程序实现,我们将区域和公司信息,统一放入公司信息表。
再来看V层JSP页面呈现:
<%@ 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方法。实现例如以下:
//定义返回给client的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 /> "); //不是叶子节点,递归调用运行 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查询的keyword值 * @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 /> "); //不是叶子节点,递归调用运行。level层次參数同步递增 readCompanyTree(conn,result.getInt("id"),level+1); }else{ sbTreeHTML.append("-") .append(result.getString("name")) .append("<br /> "); } } }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(" "); for(int i=0; i < level; i++){ sbTreeHTML.append("<img src="images/white.gif">") .append(" "); } //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(" ") .append("<img id="im"+ result.getString("id") +"" src="images/closedfold.gif">") .append(" ") .append("<a href="#">" + result.getString("name") + "</a>") .append(" ") .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(" ") .append("<img src="images/openfold.gif">") .append(" "); 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操作更是常见。写至此时。也联想到了设计模式中的组合模式,正是灵活应对树形节点增删改的好方法,后面将会进行更进一步的探讨。