运行环境:springMVC+mybatis
一、建表
说明:0表示此节点为非叶子节点,即此节点还包括了子节点;1表示此节点为叶子节点,即此节点没有子节点。
二、运用mybatis 的generator插件自动生成pojo、映射文件及访问接口并做适当的添加修改。
1.pojo
package com.shyy.web.entity; import java.util.List; public class Tree { private String id; private String text; private String iconCls; private Boolean leaf; private String fatherId; private List<Tree> children; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getIconCls() { return iconCls; } public void setIconCls(String iconCls) { this.iconCls = iconCls; } public Boolean getLeaf() { return leaf; } public void setLeaf(Boolean leaf) { this.leaf = leaf; } public String getFatherId() { return fatherId; } public void setFatherId(String fatherId) { this.fatherId = fatherId; } public List<Tree> getChildren() { return children; } public void setChildren(List<Tree> children) { this.children = children; } @Override public String toString() { return "Tree{" + "id=‘" + id + ‘\‘‘ + ", text=‘" + text + ‘\‘‘ + ", iconCls=‘" + iconCls + ‘\‘‘ + ", leaf=" + leaf + ", fatherId=‘" + fatherId + ‘\‘‘ + ", children=" + children + ‘}‘; } }
2.映射文件:
需要注意的是, <result property="leaf" column="leaf" jdbcType="TINYINT" javaType="Boolean" />这样的话,mybatis就会自动将数据库中的TINYINT类型的leaf转化成布尔类型,0会转化为false,1会转化为true。这里一定要有这个,不然在执行时会报异常。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.shyy.web.service.TreeMapper" > <resultMap id="BaseResultMap" type="com.shyy.web.entity.Tree" > <id column="id" property="id" jdbcType="VARCHAR" /> <result column="text" property="text" jdbcType="VARCHAR" /> <result column="iconCls" property="iconCls" jdbcType="VARCHAR" /> <result column="leaf" property="leaf" jdbcType="TINYINT" javaType="Boolean" /> <result column="fatherId" property="fatherId" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > id, text, iconCls, leaf, fatherId </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" > select <include refid="Base_Column_List" /> from t_tree where id = #{id,jdbcType=VARCHAR} </select> <select id="getFather" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE fatherId IS NULL </select> <select id="getChildren" resultMap="BaseResultMap" parameterType="java.lang.String"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE fatherId = #{fatherId} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.String" > delete from t_tree where id = #{id,jdbcType=VARCHAR} </delete> <insert id="insert" parameterType="com.shyy.web.entity.Tree" > insert into t_tree (id, text, iconCls, leaf, fatherId) values (#{id,jdbcType=VARCHAR}, #{text,jdbcType=VARCHAR}, #{iconCls,jdbcType=VARCHAR}, #{leaf,jdbcType=TINYINT}, #{fatherId,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.shyy.web.entity.Tree" > insert into t_tree <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id, </if> <if test="text != null" > text, </if> <if test="iconCls != null" > iconCls, </if> <if test="leaf != null" > leaf, </if> <if test="fatherId != null" > fatherId, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=VARCHAR}, </if> <if test="text != null" > #{text,jdbcType=VARCHAR}, </if> <if test="iconCls != null" > #{iconCls,jdbcType=VARCHAR}, </if> <if test="leaf != null" > #{leaf,jdbcType=TINYINT}, </if> <if test="fatherId != null" > #{fatherId,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.shyy.web.entity.Tree" > update t_tree <set > <if test="text != null" > text = #{text,jdbcType=VARCHAR}, </if> <if test="iconCls != null" > iconCls = #{iconCls,jdbcType=VARCHAR}, </if> <if test="leaf != null" > leaf = #{leaf,jdbcType=TINYINT}, </if> <if test="fatherId != null" > fatherId = #{fatherId,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=VARCHAR} </update> <update id="updateByPrimaryKey" parameterType="com.shyy.web.entity.Tree" > update t_tree set text = #{text,jdbcType=VARCHAR}, iconCls = #{iconCls,jdbcType=VARCHAR}, leaf = #{leaf,jdbcType=TINYINT}, fatherId = #{fatherId,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} </update> </mapper>
3.访问映射文件的接口(没有实现类,直接由该接口访问对应的映射文件)
package com.shyy.web.service; import com.shyy.web.entity.Tree; import java.util.List; public interface TreeMapper { int deleteByPrimaryKey(String id); int insert(Tree record); int insertSelective(Tree record); Tree selectByPrimaryKey(String id); List<Tree> getFather(); List<Tree> getChildren(String id); int updateByPrimaryKeySelective(Tree record); int updateByPrimaryKey(Tree record); }
4.控制层代码
package com.shyy.web.controller.anntation; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.shyy.web.entity.Tree; import com.shyy.web.service.TreeMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.shyy.web.controller.response.EmptyResponse; import com.shyy.web.controller.response.NormalResponse; import com.shyy.web.controller.response.Response; import com.shyy.web.entity.Privilege; import com.shyy.web.service.PrivilegeService; import com.shyy.web.service.impl.PrivilegeServiceImpl; @Controller @RequestMapping("/menu/") public class PrivilegeController { Logger logger = LoggerFactory.getLogger(PrivilegeController.class); @Autowired private TreeMapper treeMapper; @SuppressWarnings("unused") @RequestMapping("showmyMenu") @ResponseBody public List<Tree> myMenus(HttpServletRequest req, HttpServletResponse resp) { System.out.println("-aaa--"); List<Tree> pris = treeMapper.getFather();//一级菜单 List<Tree> menus = new ArrayList<Tree>(); for(Tree pri:pris){//迭代一级菜单找二级菜单 List<Tree> prisNode = treeMapper.getChildren(pri.getId());//二级菜单 pri.setChildren(prisNode); for (Tree privilege : prisNode) {//迭代二级菜单找三级菜单 List<Tree> childNode = treeMapper.getChildren(privilege.getId());//三级菜单 privilege.setChildren(childNode); //如果有四级菜单,则需迭代三级菜单找四级菜单 } menus.add(pri); } System.out.println(menus); if (menus != null || menus.size() > 0) { return menus; } else { return null; } } public String test(){ return "wang"; } }
js代码:
Ext.onReady(function(){ var model = Ext.define("TreeModel", { // 定义树节点数据模型 extend : "Ext.data.Model", fields : [{name : "id",type : "string"}, {name : "text",type : "string"}, {name : "iconCls",type : "string"}, {name : "leaf",type : "boolean"}, {name : ‘url‘,type:"string"}, {name : ‘description‘,type:"string"}] }); var store = Ext.create(‘Ext.data.TreeStore‘, { model : model,//定义当前store对象的Model数据模型 // autoLoad : true, proxy : { type : ‘ajax‘, url : ‘../menu/showmyMenu‘,//请求 reader : { type : ‘json‘, // root : ‘data‘//数据 } }, root : {//定义根节点,此配置是必须的 // text : ‘管理菜单‘, expanded : true } }); Ext.create(‘Ext.tree.Panel‘, { title: ‘Simple Tree‘, width: 200, height: 550, store: store, rootVisible: false,//隐藏根节点 renderTo: Ext.getBody() }); });
说明:在http://liuchangming1993-126-com.iteye.com/blog/1938482文章中的博主用的struts2,返回形式与本文的springMVC不同,因此这里没有指定root属性。
下面看一下js中的ajax请求返回的数据(片段):
[Tree{id=‘101‘, text=‘图像报表‘, iconCls=‘Chartbar‘, leaf=false, fatherId=‘null‘,
children=[Tree{id=‘1001‘, text=‘饼状图‘, iconCls=‘Chartpie‘, leaf=false, fatherId=‘101‘, children=[Tree{id=‘10001‘, text=‘饼状图一‘, iconCls=‘Chartpieadd‘, leaf=true, fatherId=‘1001‘, children=null}, Tree{id=‘10002‘, text=‘饼状图二‘, iconCls=‘Chartpiedelete‘, leaf=true, fatherId=‘1001‘, children=null}]},
Tree{id=‘1002‘, text=‘线状图‘, iconCls=‘Chartline‘, leaf=false, fatherId=‘101‘, children=[Tree{id=‘10003‘, text=‘线状图一‘, iconCls=‘Chartcurveadd‘, leaf=true, fatherId=‘1002‘, children=null}, Tree{id=‘10004‘, text=‘线状图二‘, iconCls=‘Chartcurvedelete‘, leaf=true, fatherId=‘1002‘, children=null}]}]}]
可以看出是reader 将对象的数据转化成了json格式。
运行效果:
方法二
这里的逻辑是一次性加载完所有的菜单返回至前台,在http://liuchangming1993-126-com.iteye.com/blog/1938482的博客中用到的是另一种方式,即根据用户点击的菜单展开子菜单。
现在用这种方式实现一下:
说明:因为之前建的表t_tree与pojo的主键都是id,发现与Extjs冲突,所以这次将表的主键改为tid,然后自动生成相应代码。
一、在映射文件中加一个sql
<select id="findLeaf" parameterType="String" resultMap="BaseResultMap"> SELECT <include refid="Base_Column_List" /> FROM t_tree WHERE 1=1 <choose> <when test="_parameter!=null and _parameter!=‘‘"> and fatherId = #{fatherId} </when> <otherwise> and fatherId IS NULL </otherwise> </choose> </select>
二、修改访问映射文件的接口,即增加访问上面sql的方法
package com.shyy.web.service; import com.shyy.web.entity.Tree; import java.util.List; public interface TreeMapper { int deleteByPrimaryKey(String tid); int insert(Tree record); int insertSelective(Tree record); Tree selectByPrimaryKey(String tid); List<Tree> getFather(); List<Tree> getChildren(String id); List<Tree> findLeaf(String id); int updateByPrimaryKeySelective(Tree record); int updateByPrimaryKey(Tree record); }
三、修改控制器,增加一个方法
@SuppressWarnings("unused") @RequestMapping("showmyMenu2") @ResponseBody public List<Tree> myMenus2(HttpServletRequest req, HttpServletResponse resp,@RequestParam String tid) { System.out.println("tid:"+tid); List<Tree> menus = new ArrayList<Tree>(); menus = treeMapper.findLeaf(tid); if (menus != null || menus.size() > 0) { return menus; } else { return null; } }
四、修改js
Ext.onReady(function(){ var store = Ext.create(‘Ext.data.TreeStore‘, { autoLoad : true, proxy : { type : ‘ajax‘, url : ‘../menu/showmyMenu2‘,//请求 reader : { type : ‘json‘, // root : ‘menuList‘//数据 }, //传参 extraParams : { tid : ‘‘ } }, root : { // text : ‘管理菜单‘, expanded : true }, listeners : { ‘beforeexpand‘ : function(node,eOpts){ //点击父亲节点的菜单会将节点的id通过ajax请求,将到后台 this.proxy.extraParams.tid = node.raw.tid; } } }); Ext.create(‘Ext.tree.Panel‘, { renderTo : Ext.getBody(), title : ‘动态加载TreePanel‘, width : 300, height : 500, useArrows : true, rootVisible: false, store : store }); });
注:
1.autoLoad 这个配置默认是false,但是即使注释掉此配置也是没有影响的;
2.可以看出上面的js已去掉了model 配置,是没有影响的,在Ext.data.TreeStore的API中有这样的说明:
如果未指定模型Model,将创建实现于Ext.data.NodeInterface的一种隐式模型。 标准的Tree字段列表也将被复制到Model上来保持自身的状态。 在Ext.data.NodeInterface文档中列出了这些字段。
那么model是干嘛的(如上面的创建的继承于Ext.data.Model的实例"TreeModel"),NodeInterface又是干嘛的?下面看一下Ext.data.NodeInterface的API简介:
本类是一个应用到Model的原型上的方法集合,使其具有Node API。这意味着树状模型 具有所有与树相关的方法。通常情况下, 开发者不会直接使用本类。为了保存树的状态和UI, 本类会在模型上创建一些额外的字段(如果它们不存在)。 这些字段记录为config选项。
到这里model的作用就明确了,举例来说,像本文定义的model,比如它有一个字段名为“leaf”,那么由于在Ext.data.NodeInterface中也具有这个配置属性,所以这个leaf的值会在加载时起作用。其他定义的字段在树面板上起到什么作用则均可在Ext.data.NodeInterface找到答案。
在api中可以看出Ext.data.TreeStore继承了Ext.data.NodeStore,而Ext.data.NodeStore的接口正是Ext.data.NodeInterface。
3.上面js中listeners 定义的‘beforeexpand’事件也来源于Ext.data.NodeInterface。在本节点展开前触发。
4.reader 中的root配置,在Ext.data.TreeStore的API中有这样的说明:
对于树读取内嵌数据,在Ext.data.reader.Reader中必须配置一个root属性, 这样reader可以找到关于每个节点的内嵌数据。 如果未指定root,那么它将默认为‘children‘。
这里介绍它的作用是“读取内嵌数据”,我并未指定,然而它说默认为‘children’,刚好我在设计菜单的pojo时对于子菜单属性的引用名就是children:
private List<Tree> children;
所以如果上面的引用是别的什么,那么就要指定了?——这里不作测试了。
运行效果:
Extjs4 treePanel异步加载菜单(后台从数据库读取)
原文:http://www.cnblogs.com/wql025/p/5040165.html