本文介绍Struts2、Spring、Hibernate与easyui的TreeGrid结合,以树的形式展现数据库中数据。在开发中会碰到很多需要以树的形式展现数据,如导航条、权限管理模块中的资源管理,通常在数据库中都是以树的形式存在。例子程序以资源管理为例,使用treegrid展现资源管理子模块的数据。最终效果如下图:
使用easyui使用树形展现很简单,只需将<table>标签的class设为easyui-treegrid即可
<body class="easyui-layout" data-options="fit:true,border:false"> <div> <span style="white-space:pre"> </span><table id="resourcelist" class="easyui-treegrid" data-options="url:'privilegemgmt/resourceAction_getAll.action',idField:'id',treeField:'resourceName',toolbar:'#tb',border:false"> <thead> <tr> <th data-options="field:'id',width:40">编号</th> <th data-options="field:'resourceName',width:150">资源名称</th> <th data-options="field:'resourceUrl',width:200">资源路径</th> <th data-options="field:'resourceOrder',width:50">排序</th> <th data-options="field:'icon',width:80">图标</th> <th data-options="field:'resourceType',width:80">资源类型</th> <th data-options="field:'parentid',width:80">上级资源ID</th> <th data-options="field:'enable',width:50">状态</th> <th data-options="field:'action',width:120">操作</th> </tr> </thead> <span style="white-space:pre"> </span></table> <span style="white-space:pre"> </span><div id="tb"> <span style="white-space:pre"> </span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-add" plain="true" onclick="onAdd()">增加</a> <span style="white-space:pre"> </span> <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-edit" plain="true" onclick="onUpdate()">编辑</a> <span style="white-space:pre"> </span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" onclick="onDelete()">删除</a> <span style="white-space:pre"> </span></div> </div> </body>
以上就为如图所示的树形展现html代码,data-options定义的各属性值作用为:
url:定义远程获取数据的路径,数据以json格式返回
idField:为树节点的标识,该值唯一
treeField:定义节点的字段,如以上定义的节点字段为资源名称,当该节点下包含子节点时,资源名称前面显示一个可展开的图标
toolbar:工具栏,传入的参数为html标签的id值
border:是否显示边框
field:字段名,即该列所需展现的数据,easyui通过解析json字符串,循环输出到表单中,根据字段名与json字符串中字段的匹配确定哪项数据输出到哪列
formatter:单元格formatter(格式化器)函数,为该列的数据显示做格式化操作,达到自己想要的显示效果。函数包含三个参数:value(字段值),rowData(行记录数据),rowIndex(行索引)。
接下来就是访问Action从服务器端获取数据。以上调用了ResourceAction的getAll方法来获取这可资源树。
ResourceAction.java
public class ResourceAction extends BaseAction { private Resource resource; private ResourceServiceI resourceService; public void getAll() { String json = resourceService.getResourceTreeToJson(); this.write(json); } //getter or setter public Resource getResource() { return resource; } public void setResource(Resource resource) { this.resource = resource; } public ResourceServiceI getResourceService() { return resourceService; } public void setResourceService(ResourceServiceI resourceService) { this.resourceService = resourceService; } }
BaseAction.java
public abstract class BaseAction extends ActionSupport { public void write(String json) { try { ServletActionContext.getResponse().setCharacterEncoding("UTF-8"); ServletActionContext.getResponse().setContentType("text/json"); ServletActionContext.getResponse().getWriter().write(json); ServletActionContext.getResponse().getWriter().flush(); ServletActionContext.getResponse().getWriter().close(); } catch (Exception ex) { Debuger.log("BaseAction write json throw Exception!"); ex.printStackTrace(); } } public void write(boolean bool) { try { String json = "{\"success\":\"" + bool + "\"}"; ServletActionContext.getResponse().getWriter().write(json); ServletActionContext.getResponse().getWriter().flush(); ServletActionContext.getResponse().getWriter().close(); } catch (Exception ex) { Debuger.log("BaseAction write bool throw Exception!"); ex.printStackTrace(); } } }
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="priviligemgmt" extends="struts-default" namespace="/privilegemgmt"> <action name="resourceAction_*" class="com.wzh_1208.sshe.action.ResourceAction" method="{1}"> <result name="input">/jsp/privilegemgmt/resourceinfo.jsp</result> </action> </package> </struts>
在使用框架提供的类时,我一般将他封装成自己的一个抽象类,然后需要使用到该类的地方就直接继承自己的基类,方便拓展和代码的重用。由于可能在项目中大多数Action都需要向客户端写出json字符串,这里就在BaseAction中封装了两个write方法,其他Action继承BaseAcion即可。getAll方法其实就一个壳子供jsp页面调用,全部的逻辑丢给了业务逻辑层的getResourceTreeToJson方法,该方法将数据库中的资源封装成了一颗树,并以json字符串的形式返回。
ResourceServiceImpl.java
import java.util.List; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import com.wzh_1208.waimai.bean.Resource; import com.wzh_1208.waimai.dao.ResourceDAOI; import com.wzh_1208.waimai.services.ResourceServiceI; public class ResourceServiceImpl implements ResourceServiceI { private ResourceDAOI resourceDao; @Override public List<Resource> getAllResource() { return resourceDao.findAll(); } @Override public String getResourceTreeToJson() { return this.createTreeJson(getAllResource()); } /** * 创建一颗树,以json字符串形式返回 * @param list 原始数据列表 * @return 树 */ private String createTreeJson(List<Resource> list) { JSONArray rootArray = new JSONArray(); for (int i=0; i<list.size(); i++) { Resource resource = list.get(i); if (resource.getParentid() == null) { JSONObject rootObj = createBranch(list, resource); rootArray.add(rootObj); } } return rootArray.toString(); } /** * 递归创建分支节点Json对象 * @param list 创建树的原始数据 * @param currentNode 当前节点 * @return 当前节点与该节点的子节点json对象 */ private JSONObject createBranch(List<Resource> list, Resource currentNode) { /* * 将javabean对象解析成为JSON对象 */ JSONObject currentObj = JSONObject.fromObject(currentNode); JSONArray childArray = new JSONArray(); /* * 循环遍历原始数据列表,判断列表中某对象的父id值是否等于当前节点的id值, * 如果相等,进入递归创建新节点的子节点,直至无子节点时,返回节点,并将该 * 节点放入当前节点的子节点列表中 */ for (int i=0; i<list.size(); i++) { Resource newNode = list.get(i); if (newNode.getParentid()!=null && newNode.getParentid().compareTo(currentNode.getId()) == 0) { JSONObject childObj = createBranch(list, newNode); childArray.add(childObj); } } /* * 判断当前子节点数组是否为空,不为空将子节点数组加入children字段中 */ if (!childArray.isEmpty()) { currentObj.put("children", childArray); } return currentObj; } //getter or setter public ResourceDAOI getResourceDao() { return resourceDao; } public void setResourceDao(ResourceDAOI resourceDao) { this.resourceDao = resourceDao; } }
在使用easyui的treegrid时,最重要的一点就是服务器为easyui返回的json数据的格式。easyui在处理从服务器端返回来的json字符串时,会根据节点下面是否包含children字段及是否含有数据来确定当前节点是否包含子节点。所以在返回json数据时,需要为含有子节点的节点加上children字段,并将子节点列表赋给该值。ResourceServiceImpl为ResourceServiceI的实现类,实现了getAllResource()和getResourceToJson()方法,getAllResource()方法调用数据库访问对象ResourceDAOImpl来获取数据库中所有的资源列表并返回,getResourceToJson方法为获取所有的Resource并组织成一颗树,以json字符串的形式返回。
ResourceDAOImpl.java
import java.sql.Timestamp; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.wzh_1208.waimai.BaseDAO; import com.wzh_1208.waimai.bean.Resource; import com.wzh_1208.waimai.dao.ResourceDAOI; public class ResourceDAOImpl extends BaseDAO implements ResourceDAOI { private static final Logger log = LoggerFactory .getLogger(ResourceDAOImpl.class); protected void initDao() { // do nothing } public List<Resource> findAll() { log.debug("finding all Resource instances"); try { //查询资源记录,按资源父id和序号的升序排列 String queryString = "from Resource order by parentid,resourceOrder asc"; return getHibernateTemplate().find(queryString); } catch (RuntimeException re) { log.error("find all failed", re); throw re; } } }
BaseDAO.java
import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public abstract class BaseDAO extends HibernateDaoSupport { }
ResourceDAOImpl继承了BaseDAO类和ResourceDAOI,并实现findAll方法,为业务逻辑层返回按资源父id和序号升序排列的Resource所有记录。
以上就完成了整个数据展现过程的实现,但工作还未完成,还需配置一大堆配置文件。这里就不贴出来了。
Resource.java
import java.sql.Timestamp; /** * Resource entity. @author WZH */ public class Resource implements java.io.Serializable { //主键id private Integer id; //资源名称 private String resourceName; //资源访问路径 private String resourceUrl; //资源描述 private String description; //资源的图标 private String icon; //父节点id private Integer parentid; //资源的类型,菜单或按钮 private String resourceType; //是否可用 private Integer enable; //资源排序序号 private Integer resourceOrder; //创建时间 private Timestamp createTime; <span style="white-space:pre"> /** default constructor */ <span style="white-space:pre"> </span>public Resource() { <span style="white-space:pre"> </span>}</span> //getter or setter方法省略 ........ }
Resource.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.wzh_1208.sshe.bean.Resource" table="resource"> <id name="id" type="java.lang.Integer"> <column name="id" /> <generator class="native"></generator> </id> <property name="resourceName" type="java.lang.String"> <column name="resource_name" length="20" not-null="true"> </column> </property> <property name="resourceUrl" type="java.lang.String"> <column name="resource_url" length="200"> </column> </property> <property name="description" type="java.lang.String"> <column name="description" length="200"> </column> </property> <property name="icon" type="java.lang.String"> <column name="icon" length="200"> </column> </property> <property name="parentid" type="java.lang.Integer"> <column name="parentid"> </column> </property> <property name="resourceType" type="java.lang.String"> <column name="resource_type" length="11" not-null="true"> </column> </property> <property name="enable" type="java.lang.Integer"> <column name="enable" not-null="true"> </column> </property> <property name="resourceOrder" type="java.lang.Integer"> <column name="resource_order"> </column> </property> <property name="createTime" type="java.sql.Timestamp"> <column name="create_time" length="0"> </column> </property> </class> </hibernate-mapping>
运行程序,数据便可以树的形式展现出来,效果图如下:
但此时只是将数据库中的数据原封不动的显示在了界面上,并未像文章开头那张图一样在节点上显示设置的图标、状态为可用或禁用以及操作栏未显示操作。此时就需为数据进行格式话处理,即用到easyui的formatter属性来定义格式化列的显示函数,来实现数据自定义的显示。修改之后的html代码如下:
<script> /** *格式化资源显示名称,easyui在显示节点字段时,如果记录中有iconCls值,easyui会根据该值来相应的图标, *如果没有则应默认图标代替。这里在传递过来的json字符串中未含有iconCls字段,但包含icon字段,这里只需 *将icon字段值赋给iconCls,即可显示图标。 **/ function formatName(value, row, index) { //在名称前面显示图标就是靠iconCls属性,iconCls属性为一个css类,easyui拿到这个属性值就能显示相应的图标了 //由于传递过来的json字符串中未包含iconCls属性,只有icon属性,所以要想easyui显示图标只需将icon的值赋给iconCls row.iconCls = row.icon; return value; } /** *格式化父资源的显示,为0时不显示父资源 **/ function formatParentId(value, row, index) { if (value == 0) { return null; } return value; } /** *格式化状态,如果为1,显示正常,为0显示禁用 */ function formatState(value,row,index) { switch (value) { case 1: return '正常'; case 0: return '禁用'; } } /** *格式化操作,在每行的操作栏显示编辑和删除操作 */ function formatAction(value, row, index) { var str = ''; if (true) { str += '<a href="javascript:onUpdate()" >编辑</a>'; } str += ' | '; if (true) { str += '<a href="javascript:onDelete()">删除</a>'; } return str; } </script> </head> <body class="easyui-layout" data-options="fit:true,border:false"> <div> <span style="white-space:pre"> </span><table id="resourcelist" class="easyui-treegrid" data-options="url:'privilegemgmt/resourceAction_getAll.action',idField:'id',treeField:'resourceName',toolbar:'#tb',border:false"> <thead> <tr> <th data-options="field:'id',width:40">编号</th> <th data-options="field:'resourceName',width:150,formatter:formatName">资源名称</th> <th data-options="field:'resourceUrl',width:200">资源路径</th> <th data-options="field:'resourceOrder',width:50">排序</th> <th data-options="field:'icon',width:80">图标</th> <th data-options="field:'resourceType',width:80">资源类型</th> <th data-options="field:'parentid',width:80,formatter:formatParentId">上级资源ID</th> <th data-options="field:'enable',width:50,formatter:formatState">状态</th> <th data-options="field:'action',width:120,formatter:formatAction">操作</th> </tr> </thead> <span style="white-space:pre"> </span></table> <span style="white-space:pre"> </span><div id="tb"> <span style="white-space:pre"> </span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-add" plain="true" onclick="onAdd()">增加</a> <span style="white-space:pre"> </span> <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-edit" plain="true" onclick="onUpdate()">编辑</a> <span style="white-space:pre"> </span><a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" onclick="onDelete()">删除</a> <span style="white-space:pre"> </span></div> </div> </body>
从以上的代码可以看出为需要格式化的列的<th>标签的data-options属性中增加了formatter属性以及对应调用的格式化函数,具体函数的作用和实现已在代码中做出说明。只是需要注意的是,iconCls字段值为css的一个类,可以使用easyui所提供的图标,也可使用自定义的图标,若使用自定义的图标时,一定要特别注意图标的路径,否则也不能正常显示。
这样就完成了Treegrid属性展现资源数据。
源码下载:http://download.csdn.net/detail/a78460750/7811961