实现web树

在我的博客数据库表的设计——自连接表的设计中,用一个商品分类表来说明怎样去设计一个无限极分类的表。如果我们要使用这样的一个无限极分类的表来实现web树,不可避免就要递归,如果树的层次很深,那么递归的次数就会很多,这时候极容易导致内存溢出。这样的表理论上可以保存无限极的分类,但在实际开发里面是不行的,因为树的层次过多,那么递归的次数就会过多,这样很容易导致内存溢出,又因为我们的计算机内存是有限的。

那么该如何设计数据库表来实现web树呢?

设计实现web树的数据库表

我们可以将商品分类设计成如下的树状结构:

再将其改造成如下样式:

树状节点的特点:

  1. 每一个节点都有一个左右值。
  2. 如果右值-左值=1,则代表当前节点为叶子节点。
  3. 如果右值-左值>1,则代表当前节点有孩子节点,值在左右值之间的所有节点,即为当前结点的所有孩子节点。

根据以上树状结构,我们可以使用如下SQL建表语句来设计数据库表:

create table category
(
    id varchar(40) primary key,
    name varchar(100),
    lft int,
    rgt int
);

并向category表中插入一些数据:

insert into category values(‘1‘,‘商品‘,1,18);
insert into category values(‘2‘,‘平板电视‘,2,7);
insert into category values(‘3‘,‘冰箱‘,8,11);
insert into category values(‘4‘,‘笔记本‘,12,17);
insert into category values(‘5‘,‘长虹‘,3,4);
insert into category values(‘6‘,‘索尼‘,5,6);
insert into category values(‘7‘,‘西门子‘,9,10);
insert into category values(‘8‘,‘thinkpad‘,13,14);
insert into category values(‘9‘,‘dell‘,15,16);

这时就会产生一个问题:为了在页面中显示树状结构,需要得到所有结点,以及每个结点在树中的层次。

解决思路:

  1. 要得到结点的层次,就是看节点有几个父亲,例如长虹有2个父亲,则它所在层次就为2。
  2. 如何知道每一个节点有几个父亲呢?这个表有个特点,父亲和孩子都在同一个表中,为得到父亲所有的孩子,可以把这张表想像成两张表,一张用于保存父亲,一张表保存孩子,如下所示:
    select * from category parent,category child;
  3. 父亲下面的孩子有个特点,它的左值>父亲的左值,并且<父亲的右值,如下所示:
    select * from category parent,category child where child.lft>=parent.lft and child.rgt<=parent.rgt;

    以上语句会得到父亲下面所有的孩子。

  4. 对父亲所有孩子的姓名进行归组,然后使用count统计函数,这时就会知道合并了几个孩子,合并了几个孩子姓名,这个孩子就有几个父亲,从而知道它所在的层次。
    select child.name,count(child.name) depth from category parent,category child where child.lft>=parent.lft and child.rgt<=parent.rgt group by child.name;
  5. 最后根据左值排序即可。
    select child.name,count(child.name) depth from category parent,category child where child.lft>=parent.lft and child.rgt<=parent.rgt group by child.name order by child.lft;

我本人使用的是mysql 5.7.11,使用如下查询语句:

select child.id,child.name,child.lft,child.rgt,count(child.name) depth from category parent,category child where child.lft>=parent.lft and child.rgt<=parent.rgt group by child.name order by child.lft;

进行查询时,会报如下错误:

[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column ‘day17.child.lft‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

解决方法如下:

  • 找到my.ini文件。

  • 在my.ini文件里面设置
    sql_mode=‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION‘

    如下图:

  • 重启MySQL服务,问题就可以解决了!

实现web树

现在我们来写代码实现web树。

创建MVC架构的Web项目

在Eclipse中新创建一个day17_tree的Web项目,导入项目所需要的开发包(jar包),创建项目所需要的包,在java开发中,架构的层次是以包的形式体现出来的。

项目所需要的开发包(jar包)

序号 开发包名称 描述
1 stl-1.2.jar jstl标签库和EL表达式依赖包
2 mysql-connector-java-5.1.38-bin.jar MySQL数据库驱动包
3 commons-dbcp-1.4.jar java配置dbcp数据库连接池使用的包
4 commons-pool-1.6.jar DBCP依赖的jar包
5 commons-dbutils-1.6.jar Apache组织提供的一个开源JDBC工具类库

项目所需要的包

序号 包名 描述 所属层次
1 cn.itcast.domain 存放系统的JavaBean类(只包含简单的属性以及属性对应的get和set方法,不包含具体的业务处理方法),提供给【数据访问层】、【业务逻辑层】、【Web层】来使用 domain(域模型)层
2 cn.itcast.dao 在此小型工程中只存放访问数据库的操作接口的实现类 数据访问层
3 cn.itcast.service 在此小型工程中只存放处理系统业务接口的实现类 业务逻辑层
4 cn.itcast.web.controller 存放作为系统控制器的Servlet(处理请求的servlet) Web层(表现层)
5 cn.itcast.utils 存放系统的通用工具类,提供给【数据访问层】、【业务逻辑层】、【Web层】来使用

以上就是根据此项目的实际情况创建的包,可能还需要创建其他的包,这个得根据项目的需要来定了。

由于我们在应用程序中加入了dbcp连接池,所以还应在类目录下加入dbcp的配置文件:dbcpconfig.properties。dbcpconfig.properties的配置信息如下:

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day17
username=root
password=yezi

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED

创建好的项目架构如下图所示:

分层架构的代码编写

分层架构的代码也是按照【域模型层(domain)】→【数据访问层(dao、dao.impl)】→【业务逻辑层(service、service.impl)】→【表现层(web.controller、web.UI、web.filter、web.listener)】→【工具类(util)】→【测试类(junit.test)】的顺序进行编写的。

开发domain层

在cn.itcast.domain包下创建一个Category类。

Category类的具体代码如下:

public class Category {

    private String id;
    private String name;
    private int lft;
    private int rgt;
    private int depth;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getLft() {
        return lft;
    }
    public void setLft(int lft) {
        this.lft = lft;
    }
    public int getRgt() {
        return rgt;
    }
    public void setRgt(int rgt) {
        this.rgt = rgt;
    }
    public int getDepth() {
        return depth;
    }
    public void setDepth(int depth) {
        this.depth = depth;
    }

}

开发数据访问层(dao、dao.impl)

在开发数据访问层之前,先在cn.itcast.utils包下创建一个获取数据库连接的工具类(JdbcUtils)。

该工具类(JdbcUtils)的代码为:

public class JdbcUtils {
    private static DataSource ds = null;

    static {
        try {
            Properties prop = new Properties();
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            prop.load(in);
            BasicDataSourceFactory factory = new BasicDataSourceFactory();
            ds = factory.createDataSource(prop);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static DataSource getDataSource() {
        return ds;
    }

    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    /*
     * 工具类里面现在没有必要提供release()方法,因为我们是使用dbutils操作数据库,
     * 即调用dbutils的update()和query()方法操作数据库,他操作完数据库之后,会自动释放掉连接。
     */
}

提示:工具类里面现在没有必要提供release()方法,因为我们是使用DBUtils操作数据库,即调用DBUtils的update()和query()方法操作数据库,它操作完数据库之后,会自动释放掉连接。

在cn.itcast.dao包下创建一个CategoryDao类。

CategoryDao类的具体代码如下:

public class CategoryDao {

    public List<Category> getAll() {

        try {
            QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());
            String sql = "select child.id,child.name,child.lft,child.rgt,count(child.name) depth from category parent,category child where child.lft>=parent.lft and child.rgt<=parent.rgt group by child.name order by child.lft";
            List<Category> list = (List<Category>) runner.query(sql, new BeanListHandler(Category.class));

            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}

照理说,开发完数据访问层,一定要对程序已编写好的部分代码进行测试。但我们有信心以上代码都不会有任何问题,这点自信都没有,搞鬼啊!

开发service层(service层对web层提供所有的业务服务)

在cn.itcast.service包下创建一个BusinessService类。

BusinessService类的具体代码如下:

public class BusinessService {

    public List<Category> getAllCategory() {

        CategoryDao dao = new CategoryDao();
        return dao.getAll();

    }
}

同理,开发完业务逻辑层,一定要对程序已编写好的部分代码进行测试,但我们有信心业务逻辑层的代码没有任何问题,所以我们略过测试这一步。

开发Web层

实现web树

在cn.itcast.web.controller包中创建一个Servlet——ListTreeServlet,用于处理实现web树的请求。

ListTreeServlet类的具体代码如下:

public class ListTreeServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        BusinessService service = new BusinessService();
        List<Category> list = service.getAllCategory();
        request.setAttribute("list", list);

        request.getRequestDispatcher("/listtree.jsp").forward(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

接下来就要创建显示web树的界面(listtree.jsp)了,该页面的创建有一点麻烦,需要我们从网上下载一个树控件:

解压该压缩包,将我们需要的xtree.js文件、xtree.css文件、images文件夹拷到我们的项目中,如下:

关于这个树控件怎么使用,可以参考其文档xtree/usage.html。在这里就不详细讲解其使用方法了。我们直接给出listtree.jsp页面的代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!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">
<title>Insert title here</title>

<!-- The xtree script file -->
<script src="${pageContext.request.contextPath }/js/xtree.js"></script>

<!-- Modify this file to change the way the tree looks -->
<link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath }/css/xtree.css">

</head>
<body>
    <script type="text/javascript">
        <c:forEach var="c" items="${list }">
            <c:if test=‘${c.depth==1 }‘>
                var tree = new WebFXTree(‘${c.name }‘);
            </c:if>

            <c:if test=‘${c.depth==2 }‘>
                var node${c.depth} = new WebFXTreeItem(‘${c.name }‘); // node2
                tree.add(node${c.depth});
            </c:if>

            <c:if test=‘${c.depth>2 }‘>
                var node${c.depth} = new WebFXTreeItem(‘${c.name }‘); // node3
                node${c.depth-1}.add(node${c.depth});    // node2.add(node3)
            </c:if>
        </c:forEach>

        document.write(tree);
    </script>
</body>
</html>

至此,实现web树的整个项目就圆满完成,现在我们来测试一把,测试结果如下:

可能出现的问题:当我们在浏览器中输入访问地址http://localhost:8080/day17_tree/ListTreeServlet访问服务器,显示web树时,文件夹图片显示不出来,那一定是图片的路径写错了。xtree.js文件里面引用图片采用的是相对路径,那么相对路径相对的是谁呢?

答:相对的是http://localhost:8080/day17_tree这个目录,该目录的下是有images目录的,所以我们显示web树是没有任何问题的。

时间: 2024-11-12 08:08:15

实现web树的相关文章

Codeforces 216D Spider&#39;s Web 树状数组+模拟

题目链接:http://codeforces.com/problemset/problem/216/D 题意: 对于一个梯形区域,如果梯形左边的点数!=梯形右边的点数,那么这个梯形为红色,否则为绿色, 问: 给定的蜘蛛网中有多少个红色. 2个树状数组维护2个线段.然后暴力模拟一下,因为点数很多但需要用到的线段树只有3条,所以类似滚动数组的思想优化内存. #include<stdio.h> #include<iostream> #include<string.h> #in

小米路由研究之中的一个加入菜单

openWRT之小米路由luci之controller 在controller下有非常多目录他们都独立的建立对应的树: Web:index.htm   --注冊了web树结点下的非常多枝叶 Mobile:index    --注冊了mbile下的一些结点 Dispatch:        ... Api:            ... Sevice:         ... 依据我们之前原生的openWRT下环境能够得知.假设我们想要要在web界面创建一个选项.我们须要做几个主要的步骤: 1.在

部署Cacti监控平台

1 部署Cacti监控平台 1.1 问题 本案例要求部署一台Cacti监控主机,并安装相关监控组件,为进一步执行具体的监控任务做准备: 安装net-snmp.net-snmp-utils 安装LAMP及相关依赖软件包 部署Cacti监控平台 初始化监控页面 1.2 方案 使用1台RHEL6虚拟机,安装部署LAMP环境.Cacti及相关的snmp组件包,配置数据库并对Cacti监控平台进行初始化操作. 1.3 步骤 实现此案例需要按照如下步骤进行. 步骤一:准备基础软件包 1)安装LAMP环境 C

小米路由研究之一添加菜单

openWRT之小米路由luci之controller 在controller下有很多文件夹他们都独立的建立相应的树: Web:index.htm   --注册了web树结点下的很多枝叶 Mobile:index    --注册了mbile下的一些结点 Dispatch:        ... Api:            ... Sevice:         ... 根据我们之前原生的openWRT下环境可以得知,如果我们想要要在web界面创建一个选项,我们需要做几个基本的步骤: 1.在c

工控SCADA模型 基于HTML5 Canvas WebGL制作摩托车

工业方面制作图表,制作模型方面运用到 3d 模型是非常多的,在一个大的环境中,构建无数个相同的或者不同的模型,构建起来对于程序员来说也是一件相当头疼的事情,我们利用 HT 帮大家解决了很大的难题,以下例子可在官网上查找到http://hightopo.com/demos/index.html: 这些都是工业控制上的一些实例,只要你有想法,你就能把这些实现得更好. 本文 Demo 地址: http://hightopo.com/guide/guide/plugin/obj/examples/exa

Security基础(五):部署Cacti监控平台、构建Cacti监测系统

一.部署Cacti监控平台 目标: 本案例要求部署一台Cacti监控主机,并安装相关监控组件,为进一步执行具体的监控任务做准备: 安装net-snmp.net-snmp-utils 安装LAMP及相关依赖软件包 部署Cacti监控平台 初始化监控页面 方案: 使用1台RHEL7虚拟机,安装部署LAMP环境.Cacti及相关的snmp组件包,配置数据库并对Cacti监控平台进行初始化操作. 步骤: 步骤一:准备基础软件包 1)安装LAMP环境 Cacti监控需要通过Web页面展示出来,并且还需要使

day17 dbutils 和 jdbc 多表操作

day17 dbutils 和 jdbc 多表操作 1. dbutils 框架简化 jdbc 开发 1.1 ResultSetHandler 接口的实现类 2. 用 dbutils 进行事务管理 2.1 正常开发中的转账实现 2.2 ThreadLocal - 线程范围内共享数据 3. jdbc 多表操作(1:n) 3.1 jdbc 多表操作 - 级联 4. jdbc 多表操作(n:m) 5. web 树 Author:相忠良 Email: [email protected] 起始于:June

构建Cacti监测系统

案例1:部署Cacti监控平台 案例2:构建Cacti监测系统 1 案例1:部署Cacti监控平台1.1 问题 本案例要求部署一台Cacti监控主机,并安装相关监控组件,为进一步执行具体的监控任务做准备: 安装net-snmp.net-snmp-utils 安装LAMP及相关依赖软件包 部署Cacti监控平台 初始化监控页面 1.2 方案 使用1台RHEL7虚拟机,安装部署LAMP环境.Cacti及相关的snmp组件包,配置数据库并对Cacti监控平台进行初始化操作.1.3 步骤 实现此案例需要

基于MVC+EasyUI的Web开发框架经验总结(2)- 使用EasyUI的树控件构建Web界面

最近花了不少时间在重构和进一步提炼我的Web开发框架上,力求在用户体验和界面设计方面,和Winform开发框架保持一致,而在Web上,我主要采用EasyUI的前端界面处理技术,走MVC的技术路线,在重构完善过程中,很多细节花费不少时间进行研究和提炼,一步步走过来,也积累了不少经验,本系列将主要介绍我在进一步完善我的Web框架基础上积累的经验进行分享,本随笔主要介绍使用EasyUI的树控件构建Web界面的相关经验. 在很多界面设计上,我们可能都需要引入树列表控件,这个控件可以用zTree来实现,也