zoukankan      html  css  js  c++  java
  • Web中树形数据(层级关系数据)的实现—以行政区树为例

    在Web开发中常常遇到树形数据的操作,如菜单、组织机构、行政区(省、市、县)等具有层级关系的数据。

    以下以行政区为例说明树形数据(层级关系数据)的存储以及实现,效果如图所看到的。



    1 数据库表结构设计


    树形数据一般通过父节点和子节点实现数据之间的层级关联,层级关系在数据库中主要通过主键和外键来实现。

    --使用Oracle数据库
    --创建行政区表
    create table TB_XZQ
    (
      code         NUMBER not null,  --行政区编码,主键
      parent_code  NUMBER,           --上级行政区编码,假设没有上级行政区。则为空
      name         VARCHAR2(50)      --行政区名称  
    );
    
    --设置CODE为主键
    alter table TB_XZQ add constraint PK_TB_XZQ primary key (CODE) using index;
    
    --设置外键
    alter table TB_XZQ
      add constraint FK_TB_XZQ_PARENT_CODE foreign key (PARENT_CODE)
      references TB_XZQ (CODE) on delete set null;
    
    --插入行政区数据
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420000, NULL, '湖北省');
    
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420100, 420000, '武汉市');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420101, 420100, '市辖区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420102, 420100, '江岸区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420103, 420100, '江汉区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420104, 420100, '硚口区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (420105, 420100, '汉阳区');
    
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (421000, 420000, '荆州市');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (421001, 421000, '市辖区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (421002, 421000, '沙市区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (421003, 421000, '荆州区');
    
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430000, NULL, '湖南省');
    
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430100, 430000, '长沙市');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430101, 430100, '市辖区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430102, 430100, '芙蓉区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430103, 430100, '天心区');
    INSERT INTO TB_XZQ (CODE,PARENT_CODE,NAME) VALUES (430104, 430100, '岳麓区');

    插入后数据例如以下图所看到的



    2 树形数据Java实现


    通常情况下,从数据库中读取的数据须要转换为树形结构。TreeNode是一个Java树形数据实现类。通过静态方法buildTree能够方便的把TreeNode构建成树。

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 树节点。支持Ext、zTree等Web控件
     * 
     * @author accountwcx@qq.com
     * @param <T> 树节点的绑定数据类
     */
    public class TreeNode<T> {
    	/**
    	 * 树节点id
    	 * 为了兼容多种情况,使用String类型
    	 */
    	private String id;
    	
    	/**
    	 * 树节点上级id
    	 */
    	private String parentId;
    	
    	/**
    	 * 树节点显示文本
    	 */
    	private String text;
    	
    	/**
    	 * 树节点名称。内容和text一样
    	 * 该字段主要是为了兼容Ext和zTree
    	 */
    	private String name;
    	
    	/**
    	 * 是否为叶子节点
    	 */
    	private Boolean leaf = true;
    	private Boolean expanded = false;
    	private T nodeData;
    	
    	/**
    	 * 是否为父节点,该字段和leaf反复,主要是为了兼容Ext和zTree
    	 */
    	private Boolean isParent = false;
    	
    	/**
    	 * 子节点。假设没有子节点,则列表长度为0
    	 */
    	private List<TreeNode<T>> children = new ArrayList<TreeNode<T>>();
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getParentId() {
    		return parentId;
    	}
    
    	public void setParentId(String parentId) {
    		this.parentId = parentId;
    	}
    
    	public String getText() {
    		return text;
    	}
    
    	public void setText(String text) {
    		this.name = text;
    		this.text = text;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    		this.text = name;
    	}
    
    	public Boolean getExpanded() {
    		return expanded;
    	}
    
    	public void setExpanded(Boolean expanded) {
    		this.expanded = expanded;
    	}
    
    	public List<TreeNode<T>> getChildren() {
    		return children;
    	}
    
    	public void setLeaf(Boolean leaf) {
    		this.leaf = leaf;
    		this.isParent = !leaf;
    	}
    
    	public Boolean getLeaf() {
    		return this.leaf;
    	}
    
    	public Boolean getIsParent() {
    		return isParent;
    	}
    
    	public void setIsParent(Boolean isParent) {
    		this.isParent = isParent;
    		this.leaf = !isParent;
    	}
    
    	public T getNodeData() {
    		return nodeData;
    	}
    
    	public void setNodeData(T nodeData) {
    		this.nodeData = nodeData;
    	}
    	
    	/**
    	 * 把树节点列表构造成树,最后返回树的根节点,假设传入的列表有多个根节点,会动态创建一个根节点。

    * @param nodes 树节点列表 * @return 根节点 */ public static <T> TreeNode<T> buildTree(List<TreeNode<T>> nodes){ if(nodes == null || nodes.size() == 0){ return null; } if(nodes.size() == 1){ return nodes.get(0); } //用来存放nodes里面的顶级树节点 //也就是把没有父节点的节点都放到tops里面去 List<TreeNode<T>> tops = new ArrayList<TreeNode<T>>(); boolean hasParent = false; //第一次遍历,获取一个节点作为子节点 for(TreeNode<T> child : nodes){ hasParent = false; //当前节点child的父节点id String pid = child.getParentId(); //假设pid不存在或为空 //则当前节点为顶级节点 if(pid == null || pid.equals("")){ //把当前节点加入到tops中作为顶级节点 tops.add(child); //跳过当前节点,进入下一轮 continue; } //遍历nodes上的全部节点,推断是否有child的父节点 for(TreeNode<T> parent : nodes){ String id = parent.getId(); //假设parent节点的id等于child节点的pid,则parent节点是child节点的父节点 if(id != null && id.equals(pid)){ //把child加到parent下 parent.getChildren().add(child); parent.setLeaf(false); //child节点有父节点 hasParent = true; continue; } } //假设child节点没有父节点。则child是顶级节点 //把child加入到tops中 if(!hasParent){ tops.add(child); } } TreeNode<T> root; if(tops.size() == 1){ //假设顶级节点仅仅有一个。该顶级节点是根节点 root = tops.get(0); }else{ //假设顶级节点有多个,创建一个根节点,把顶级节点放到根节点下 root = new TreeNode<T>(); root.setLeaf(false); root.setId("-1"); root.setName("root"); root.setParentId(""); root.getChildren().addAll(tops); } return root; } }


    3 生成行政区树


    Dao(仅仅列出主要代码)

    @Repository("xzqDao")
    public class XzqDaoImpl implements XzqDao {
    	@Resource
    	private JdbcTemplate jdbcTemplate;
    	
    	public List<XzqEntity> select() {
    		String sql = "SELECT CODE,PARENT_CODE,NAME FROM TB_XZQ";
    		
    		//用Spring JDBC进行数据库操作
    		return jdbcTemplate.query(sql, new RowMapper<XzqEntity>(){
    			
    			public XzqEntity mapRow(ResultSet rs, int index) throws SQLException {
    				XzqEntity xzq = new XzqEntity();
    				xzq.setCode(rs.getInt("CODE"));
    				xzq.setName(rs.getString("NAME"));
    				xzq.setParentCode(rs.getInt("PARENT_CODE"));
    				
    				return xzq;
    			}
    			
    		});
    	}
    }

    Service(仅仅列主要代码)

    @Service("xzqService")
    public class XzqServiceImpl implements XzqService {
    	@Resource
    	private XzqDao xzqDao;
    	
    	public TreeNode<XzqEntity> tree(){
    		List<XzqEntity> list = xzqDao.select();
    		
    		List<TreeNode<XzqEntity>> nodes = new ArrayList<TreeNode<XzqEntity>>();
    		
    		//把行政区类转为树节点
    		for(XzqEntity xzq : list){
    			TreeNode<XzqEntity> node = new TreeNode<XzqEntity>();
    			
    			//节点id
    			node.setId(xzq.getCode().toString());
    			
    			//节点上级id
    			node.setParentId(xzq.getParentCode().toString());
    			
    			node.setText(xzq.getName());
    			
    			//把行政区类放到节点数据中,以备使用
    			node.setNodeData(xzq);
    			
    			nodes.add(node);
    		}
    		
    		return TreeNode.buildTree(nodes);
    	}
    }

    Controller(仅仅列出主要代码)

    @Controller
    @RequestMapping("/xzq")
    public class XzqController {
    	@Resource
    	private XzqService xzqService;
    
    	/**
    	 * 行政区树,返回JSON格式
    	 * 
    	 * @param response
    	 */
    	@RequestMapping("/tree.mvc")
    	public void tree(HttpServletResponse response) {
    		String json = "";
    		try {
    			json = JSON.toJSONString(xzqService.tree());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    		//输出JSON数据
    		//这里直接通过response输出JSON字符串
    		//Spring MVC也提供了输出JSON数据的方法
    		
    		// 设置编码格式
    		response.setContentType("text/plain;charset=utf-8");
    		response.setCharacterEncoding("utf-8");
    
    		PrintWriter out = null;
    		try {
    			out = response.getWriter();
    			out.write(json);
    			out.flush();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }

    ztree显示行政区树

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%
    String context = request.getContextPath();
    %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="./lib/ztree/zTreeStyle.css" />
    
    <script type="text/javascript" src="./lib/jquery/jquery-1.11.2.min.js"></script>
    <script type="text/javascript" src="./lib/ztree/jquery.ztree.all-3.5.min.js"></script>
    <title>行政区</title>
    </head>
    <body>
    <!-- 行政区树 -->
    <ul class="ztree" id="xzqtree" style="180px;height:350px;margin:10px;border:1px solid blue;overflow:auto;"></ul>
    <script type="text/javascript">
    $(function(){
    	
    	//获取行政区数据
    	$.ajax({
    		url: './xzq/tree.mvc',
    		dataType: 'json'
    	}).done(function(data){
    		if(!data){
    			return;
    		}
    		
    		//初始化行政区树
    		$.fn.zTree.init($('#xzqtree'), {}, data);
    	});
    });
    </script>
    </body>
    </html>


  • 相关阅读:
    LoadRunner字符编码转换
    登山记02-百丈岭古道(昌北古道)_20201213
    JVM GC原理和监控
    登山记01_径山古道_20201107
    awk命令
    shell计算文件中某一列的平均值
    linux命令后台运行
    二维数组_基础(九)
    一维数组(八)
    选择语句switch总结(七)
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6766873.html
Copyright © 2011-2022 走看看