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 [email protected]
 * @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="width: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>
时间: 2024-12-19 18:01:00

Web中树形数据(层级关系数据)的实现—以行政区树为例的相关文章

sql中对查询出来的数据进行分页

当sql中存储的数据量比较大时,在web中 数据显示时都会对数据进行分页,分页不会在客户端进行分页,而是在数据库查询过程中进行了分页. sql代码: DECLARE @pageindex INT; --页码 DECLARE @pagesize INT; --每页显示的记录数量 SET @pageindex=; SET @pagesize=5; SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY UserName) AS rownumber,* FRO

什么是大数据?大数据有什么用?【听大神来分析!】

什么是大数据大数据技术的战略意义不在于掌握庞大的数据信息,而在于对这些含有意义的数据进行专业化处理.换而言之,如果把大数据比作一种产业,那么这种产业实现盈利的关键,在于提高对数据的"加工能力",通过"加工"实现数据的"增值". 随着云时代的来临,大数据(Big data)也吸引了越来越多的关注.分析师团队认为,大数据(Big data)通常用来形容一个公司创造的大量非结构化数据和半结构化数据,这些数据在下载到关系型数据库用于分析时会花费过多时间和

初级篇第十期:学习查看View的层级关系

学习建议:自己动手,丰衣足食 学习周期:1周 学习目的:熟练使用Debug View Hierarchy查看View的层级关系 学习答疑:欢迎来技术群里提问并做分享 学习工具:Xcode开发环境,iOS8+ 学习内容:熟悉Xcode自带视图管理工具 这个功能很强大,早起只有收费软件Reveal可以来很好的调节iOS上UI界面的问题,那么在iOS8出来以后呢,Xcode6又更新出来一个功能,算是自带工具吧,与Reveal是类似的,不管你用IB还是纯代码,都是可以通过这个工具来查看当前程序运行界面中

设计与开发一款简单易用的Web报表工具(支持常用关系数据及hadoop、hbase等)

EasyReport是一个简单易用的Web报表工具(支持Hadoop,HBase及各种关系型数据库),它的主要功能是把SQL语句查询出的行列结构转换成HTML表格(Table),并支持表格的跨行(RowSpan)与跨列(ColSpan).同时它还支持报表Excel导出.图表显示及固定表头与左边列的功能.总体架构如下图所示: 目录 开发环境(Development Environment) 安装与部署(Installation & Deployment) 从源代码安装(From Source Co

客户关系管理系统中对客户及相关数据的导入导出操作

在很多系统,我们都知道,Excel数据的导入导出操作是必不可少的一个功能,这种功能能够给使用者和外部进行数据交换,也能批量迅速的录入数据到系统中:但在一些系统中,为了方便,可能把很多个基础表或者相关的数据综合到一个Excel表格文件里面,然后希望通过接口进行导入,这种需求处理就显得比较复杂一点了.本文探讨在我的客户关系管理系统中,对于单个Excel表格中,集合了客户基础数据及相关数据的导入和导出操作的处理. 1.导入导出的需求分析 本随笔主要介绍如何在系统中,导入单一文件中的数据到系统中,这个文

element中树形数据与懒加载实现全部展开和全部收起

element中属性懒加载数据 default-expand-all属性::是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效 如果在表格头上加上一个按钮实现全部展开与收起 类似如图这种  默认是[全部展开]按钮,点击后,树状列表下所有数据为展示,按钮变为[全部收起]:点击[全部收起]时,树状列表下所有数据为收起状态,即返回默认状 如果动态设置控制default-expand-all树状图是不发生变化的 也就是不起作用. 解决方法:上代码: <template> <d

项目中树形结构的添加与立即删除该数据问题

立即添加是可以的,但是想把刚添加的那条数据删除就不行了.得不到数据的id值: 处理方法:我写了一个sql语句,在添加之后,把数据中最大的id值取出来,添加在节点上,这样就可以保证立即添加的数据,就可以立即删除了. $.ajax({ type: 'POST', url: '/yxt-admin/admin/address/insert', data:{ name:name, pid:treeNode.id, codeValue:$('#code_value').textbox('getValue'

一个有趣的SQL Server 层级汇总数据问题

看SQL Server大V宋大侠的博客文章,发现了一个有趣的sql server层级汇总数据问题. 具体的问题如下: parent_id emp_id emp_name total_amout     NULL 2 Andrew 200     2 1 Nancy 100     2 3 Janet 120     3 4 Michael 80     1 5 Robert 50     每个员工的总销售额=自己的销售额+其下级员工的总销售额,     比如:     Andrew = 200_

层次结构和二维表的关系数据存储

摘:Storing Hierarchical Data in a Database Article(翻译版本) 原文链接:http://shiningray.cn/hierarchical-data-database.html 作者:Gijs Van Tulder 翻译:ShiningRay @ NirvanaStudio 无论你要构建自己的论坛,在你的网站上发布消息还是书写自己的CMS程序,你都会遇到要在数据库中存储层次数据的情况.同时,除非你使用一种像XML的数据库,否则关系数据库中的表都不