在web开发中的树状视图技术
树型数据模型在现实生活中应用相当广泛,从超市的商品分类到政府的组织结构无不都是”树形”的。在实际的项目开发中也经常需要显示这种结构。比如,在树状视图上给一个单位的职工赋予系统操作权限。
在web开发中实现树状显示在技术上主要有以下两种方案
1. 采用js操作DOM模型构建树状视图
2. 采用HTML来表达树的节点,使用js和css来赋予动态效果
第一种方案的典型示例是网上流传的各种tree.js脚本。例如:http://www.blueshoes.org/en/javascript/tree/
这种方式在运行时由客户端浏览器动态生成树状结构。特点是操作比较简单,对二次开发人员来讲上手比较容易。缺点是js脚本运行速度缓慢。在一些需要和用户进行“强交互”的场景下而树的节点又比较多(比如上万个),每次浏览器刷新后需要”漫长”的等待来生成DOM模型,会让客户觉得不友好。
另外一个缺陷就是在树状视图上进行操作,例如在树型视图的节点上使用鼠标单双击触发动作或是生成菜单。实现这些必须要深入到js脚本中的细节中才可以订制。对开发人员来讲难度非常大。
第二种方案的特点是使用HTML元素静态表达树状节点,使用js和css样式来赋予动态效果,在HTML节点上实现菜单和鼠标触发动作。如下图所示:
大家知道浏览器渲染静态HTML数据的速度是非常快的,几百K的静态数据在浏览器里的渲染基本上就是一瞬间。在需要频繁刷新视图的情况下也基本上感觉不到延迟,甚至可以实现CS开发中”操作结果立即可见”的效果。在静态HTML元素上设置触发动作和菜单也比较方便,给开发人员自由发挥的余地较多。
这种方案的缺点是技术难度较大编码比较复杂,需要使用框架进行规范实现来克服这种弊病。
webTreeViewer框架
webTreeViewer框架可以根据数据库到树状模型的映射配置信息直接生成树状视图。用户唯一需要做的就是编写由数据库到树状数据模型的映射文件并配置到框架中去,再通过框架预定义的标签来指定视图的具体显示效果、设置菜单等。编写非常方便,基本上为二次开发人员屏蔽了所有的技术细节。二次开发人员只需要告诉框架”想要做什么”。
视图的显示速度非常快。原公司开发的项目里,在一台配置较低的商用机中同时运行ORACLE,原公司项目及客户端浏览器,在节点数达到2000个的情况下刷新视图基本感觉不到延迟。
webTreeViewer依赖HQL语言,但不是非得依赖Hibernate框架,但目前来看Hibernate是最好的选择,也可以移植到纯JDBC项目或其他持久化技术下。 webTreeViewer采用spring进行配置并作为IOC容器,但并没有同spring进行耦合。所有的配置完全可以采用手动编码来实现。
为了更好的控制js脚本和html,框架使用了freeMarker,并且使用apache –degister来解析配置项。
在视图技术上采用一些列jsp标签来具体设定显示效果。
视图依赖的所有资源包括class文件、图片、脚本等都集成在jar包中,在需要的时候框架会自动加载,不需要再引入其他文件。
框架使用介绍
基本工作原理简介
webTreeViewer框架的设计是受了SWT/JFACE treeViewer启发的,所以熟悉JFACE treeViewer的朋友对webTreeViewer应该可以很快上手。
同大部分视图技术一样, webTreeViewer也使用了MVC模型。
在模型层要做的工作就是将”树状的”存储结构映射为”树状”的数据结构。此步骤由框架操作HQL wrapper来完成.所谓HQL Wrapper就是可以执行HQL语句进行数据库查询的实例,实现I_HQLWrapper接口。
树的数据结构类似于普通的面向对象的树状数据结构。可以导航到父节点,对于子节点的集合引用等。每一个节点都有三个强制性的属性即id、layer和作为类别标识的category。另外还可以设定扩展属性properties。 Properties是一个map的结构,可以映射一个键值对,用户可以将任何存储结构中的属性映射进来。每一次需要显示时,树形结构的根节点都会被传送到视图层。在jsp中可以通过EL、ONGL、标签或其他技术来取得map中的值显示在视图。
webTreeViewer有默认的机制可以把对树状显示的要求自动转发到提取数据的模块中,生成模型之后再自动转发到用户预定义的jsp文件,并最终显示在客户端。这一切只需要”配置”即可,除了jsp页之外不需要写一行代码。对于高级用户,框架也预留了足够的灵活性。在控制层,用户可以自己手动的提取模型数据,根据自己的要求对模型数据进行修改,再手动转发到视图。可以使用任何熟悉的web框架及技术,比如struts。当然这需要额外的代码工作才能完成。
数据模型是在一个TreeViewContext 中交给视图的。在视图上树状数据模型被遍历迭代,每一个节点都要迭代给用户以决定其具体的显示效果。同SWT/JFACE treeViewer的类似,你可以设置一个过滤器和排序器来决定哪些节点最终被留下来,以及他们在一个父节点下的显示顺序如何。 webTreeViewer框架使用了跟多技术来处理视图层,比如包括对菜单的处理等,可以让HTML代码很好的同js脚本相结合,让视图的开发编写简洁高效。
视图上的一个动作被触发之后,比如注册在菜单上动作或是自定义的鼠标单击事件,都有可能刷新数据模型(数据库中的存储模型),这时可以选择刷新视图来更新显示。上面的每一个步骤将会重演,将最新的数据库信息映射成树状显示返回给浏览器;同时,客户端框架会保存之前的”状态”,比如客户打开了哪些节点,当前节点在整个视图的位置即滚动条的位置等都会被恢复。这一切都会在一瞬间完成,可以很好的模拟CS开发的效果。如果节点较多,比如有几千个甚至过万,而且服务器的效率不高时可能会有一些延迟和闪烁的问题。在下一个版本中准备采取一个类似于”双缓冲”的技术解决客户端闪烁的问题。
总而言之,我已经尽力屏蔽了所有技术细节,webTreeViewer可以做所有的这些工作,二次开发人员只需要告诉它”到底要做什么”。
下面就举一个具体实例来演示说明,实例代码可在此下载。
1. 树状模型和映射文件的编写
在模型层,框架做的基本工作就是将树状的存储结构映射为树状的数据结构。
比如,我们要做一个图书馆的项目。项目要求在浏览器中按照树状结构来展示图书,图书都是属于某个类别的,比如武侠类图书。
我们在MYSQL数据库中创建如下表:
create table SORT
( ID bigint not null auto_increment,
PARENT_ID bigint,
LAYER smallint not null,
NAME varchar(60) not null,
DISCRIPTION varchar(200),
IMAGE varchar(200),
primary key (ID)
)type = InnoDB;
alter table SORT add constraint FK_SELF_CON foreign key (PARENT_ID)
references SORT (ID) on delete restrict on update restrict;
注意到这个表是”自关联”的。任何树状存储结构的表结构都应该是自关联的,否则如何表达树形关系?在这里”类别”也是自关联的,比如武侠类属于通俗文学类,而通俗文学又属于文史类。
存储结构中以自关联形式创建的表称为树的”branch”,即树干的意思。
光有树干(树枝)还不行,还要有树叶才可以成为一棵完整的树。所有以外键引用”树干”的表结构称为leaf---树叶,而且leaf可以不只一种。
在此例中图书馆在具体的类别下面保存着两种东西——— 图书和光盘(VCD)
create table VCD
( VCD_ID bigint not null auto_increment,
TREE_ID bigint,
VCD_NAME varchar(60) not null,