项目中我们可能经常有这样的需求,需要返回二级或三级的菜单,返回一个树形结构,面试中也可能经常被问到。

最近的项目中就用到了,这里整理分享一下。

应用场景:

地区树,国家,省,市,区县共4级。

表结构:

建表语句:

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
--  Table structure for `base_area`
-- ----------------------------
DROP TABLE IF EXISTS `base_area`;
CREATE TABLE `base_area` (`ID` bigint(20) NOT NULL AUTO_INCREMENT,`AREA_NAME` varchar(255) DEFAULT NULL COMMENT '地区名称',`AREA_CODE` varchar(255) DEFAULT NULL COMMENT '地区编码',`PARENT_ID` bigint(20) DEFAULT NULL,`PLAT_MARK` bigint(20) DEFAULT NULL COMMENT '区域标识,也就是平台标识',`LEVEL` tinyint(4) DEFAULT '1' COMMENT '层',`STATUS` tinyint(4) DEFAULT '1' COMMENT '是否可用、是否显示',`EXPAND` tinyint(4) DEFAULT '0' COMMENT '是否展开子节点,非0为展开。',PRIMARY KEY (`ID`),KEY `index2` (`PLAT_MARK`)
) ENGINE=InnoDB AUTO_INCREMENT=3514 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;

返回JSON结果示例:

数据比较多,这里只展示一部分,有需要完整表数据的小伙伴,稍后提供下载地址。

{"result":[{"createTimeString":"","updateTimeString":"","level":3,"platMark":100001001000000,"parentId":1,"areaCode":"110000","expand":0,"areaName":"北京市","id":2,"childrenList":[{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001001000,"parentId":2,"areaCode":"110101","expand":0,"areaName":"东城区","id":4,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001002000,"parentId":2,"areaCode":"110102","expand":0,"areaName":"西城区","id":5,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001003000,"parentId":2,"areaCode":"110105","expand":0,"areaName":"朝阳区","id":6,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001004000,"parentId":2,"areaCode":"110106","expand":0,"areaName":"丰台区","id":7,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001005000,"parentId":2,"areaCode":"110107","expand":0,"areaName":"石景山区","id":8,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001006000,"parentId":2,"areaCode":"110108","expand":0,"areaName":"海淀区","id":9,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001007000,"parentId":2,"areaCode":"110109","expand":0,"areaName":"门头沟区","id":10,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001008000,"parentId":2,"areaCode":"110111","expand":0,"areaName":"房山区","id":11,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001009000,"parentId":2,"areaCode":"110112","expand":0,"areaName":"通州区","id":12,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001010000,"parentId":2,"areaCode":"110113","expand":0,"areaName":"顺义区","id":13,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001011000,"parentId":2,"areaCode":"110114","expand":0,"areaName":"昌平区","id":14,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001012000,"parentId":2,"areaCode":"110115","expand":0,"areaName":"大兴区","id":15,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001013000,"parentId":2,"areaCode":"110116","expand":0,"areaName":"怀柔区","id":16,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001014000,"parentId":2,"areaCode":"110117","expand":0,"areaName":"平谷区","id":17,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001015000,"parentId":2,"areaCode":"110228","expand":0,"areaName":"密云县","id":18,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100001001016000,"parentId":2,"areaCode":"110229","expand":0,"areaName":"延庆县","id":19,"childrenList":[]}]},{"createTimeString":"","updateTimeString":"","level":3,"platMark":100002001000000,"parentId":1,"areaCode":"120000","expand":0,"areaName":"天津市","id":20,"childrenList":[{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001001000,"parentId":20,"areaCode":"120101","expand":0,"areaName":"和平区","id":22,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001002000,"parentId":20,"areaCode":"120102","expand":0,"areaName":"河东区","id":23,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001003000,"parentId":20,"areaCode":"120103","expand":0,"areaName":"河西区","id":24,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001004000,"parentId":20,"areaCode":"120104","expand":0,"areaName":"南开区","id":25,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001005000,"parentId":20,"areaCode":"120105","expand":0,"areaName":"河北区","id":26,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001006000,"parentId":20,"areaCode":"120106","expand":0,"areaName":"红桥区","id":27,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001007000,"parentId":20,"areaCode":"120110","expand":0,"areaName":"东丽区","id":28,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001008000,"parentId":20,"areaCode":"120111","expand":0,"areaName":"西青区","id":29,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001009000,"parentId":20,"areaCode":"120112","expand":0,"areaName":"津南区","id":30,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001010000,"parentId":20,"areaCode":"120113","expand":0,"areaName":"北辰区","id":31,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001011000,"parentId":20,"areaCode":"120114","expand":0,"areaName":"武清区","id":32,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001012000,"parentId":20,"areaCode":"120115","expand":0,"areaName":"宝坻区","id":33,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001013000,"parentId":20,"areaCode":"120116","expand":0,"areaName":"滨海新区","id":34,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001014000,"parentId":20,"areaCode":"120221","expand":0,"areaName":"宁河县","id":35,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001015000,"parentId":20,"areaCode":"120223","expand":0,"areaName":"静海县","id":36,"childrenList":[]},{"createTimeString":"","updateTimeString":"","level":4,"platMark":100002001016000,"parentId":20,"areaCode":"120225","expand":0,"areaName":"蓟县","id":37,"childrenList":[]}]}]}

实体类:

/*** 表名:base_area* 备注:BaseArea*/
@SuppressWarnings("serial")
public class BaseArea {//date formats//columns START//idprivate Long id;//地区名称private String areaName;//地区编码private String areaCode;//parentIdprivate Long parentId;//区域标识,也就是平台标识private Long platMark;//层private Byte level;//是否可用、是否显示private Integer status;//是否展开子节点,非0为展开。private Byte expand;//columns END//extend columns START//子节点列表List<BaseArea> childrenList;//extend columns ENDpublic List<BaseArea> getChildrenList() {return childrenList;}public void setChildrenList(List<BaseArea> childrenList) {this.childrenList = childrenList;}public BaseArea(){}public BaseArea(Long id){this.id = id;}/*** id* @return*/public Long getId() {return this.id;}/*** id* @param value*/public void setId(Long value) {this.id = value;}/*** 地区名称* @return*/public String getAreaName() {return this.areaName;}/*** 地区名称* @param value*/public void setAreaName(String value) {this.areaName = value;}/*** 地区编码* @return*/public String getAreaCode() {return this.areaCode;}/*** 地区编码* @param value*/public void setAreaCode(String value) {this.areaCode = value;}/*** parentId* @return*/public Long getParentId() {return this.parentId;}/*** parentId* @param value*/public void setParentId(Long value) {this.parentId = value;}/*** 区域标识,也就是平台标识* @return*/public Long getPlatMark() {return this.platMark;}/*** 区域标识,也就是平台标识* @param value*/public void setPlatMark(Long value) {this.platMark = value;}/*** 层* @return*/public Byte getLevel() {return this.level;}/*** 层* @param value*/public void setLevel(Byte value) {this.level = value;}/*** 是否可用、是否显示* @return*/public Integer getStatus() {return this.status;}/*** 是否可用、是否显示* @param value*/public void setStatus(Integer value) {this.status = value;}/*** 是否展开子节点,非0为展开。* @return*/public Byte getExpand() {return this.expand;}/*** 是否展开子节点,非0为展开。* @param value*/public void setExpand(Byte value) {this.expand = value;}
}

Mapperxml 映射方法

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"><!-- 不使用namespace的话sql搜索定位会比较方便 -->
<!-- BaseArea -->
<mapper namespace="BaseArea"><resultMap id="baseAreaResult" type="com.wanyu.smarthome.model.BaseArea"><id property="id" column="ID"/><result property="areaName" column="AREA_NAME"/><result property="areaCode" column="AREA_CODE"/><result property="parentId" column="PARENT_ID"/><result property="platMark" column="PLAT_MARK"/><result property="level" column="LEVEL"/><result property="status" column="STATUS"/><result property="expand" column="EXPAND"/><collection property="childrenList" javaType="java.util.ArrayList" column="id"ofType="com.wanyu.smarthome.model.BaseArea" select="childrenSelect"></collection></resultMap><resultMap id="childrenResult" type="com.wanyu.smarthome.model.BaseArea"><id property="id" column="ID"/><result property="areaName" column="AREA_NAME"/><result property="areaCode" column="AREA_CODE"/><result property="parentId" column="PARENT_ID"/><result property="platMark" column="PLAT_MARK"/><result property="level" column="LEVEL"/><result property="status" column="STATUS"/><result property="expand" column="EXPAND"/><collection property="childrenList" javaType="java.util.ArrayList" column="id"ofType="com.wanyu.smarthome.model.BaseArea" select="childrenSelect"></collection></resultMap><!-- 用于select查询公用抽取的列 --><sql id="commonColumns"><![CDATA[ID,AREA_NAME,AREA_CODE,PARENT_ID,PLAT_MARK,LEVEL,STATUS,EXPAND]]></sql><sql id="commonAliasColumns"><![CDATA[x.ID,x.AREA_NAME,x.AREA_CODE,x.PARENT_ID,x.PLAT_MARK,x.LEVEL,x.STATUS,x.EXPAND]]></sql><!-- useGeneratedKeys="true" keyProperty="xxx" for sqlserver and mysql --><insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.wanyu.smarthome.model.BaseArea"><![CDATA[INSERT INTO base_area (ID,AREA_NAME,AREA_CODE,PARENT_ID,PLAT_MARK,LEVEL,STATUS,EXPAND) VALUES (#{id},#{areaName},#{areaCode},#{parentId},#{platMark},#{level},#{status},#{expand})]]></insert><update id="update" parameterType="com.wanyu.smarthome.model.BaseArea"><![CDATA[UPDATE base_area SETAREA_NAME = #{areaName},AREA_CODE = #{areaCode},PARENT_ID = #{parentId},PLAT_MARK = #{platMark},LEVEL = #{level},STATUS = #{status},EXPAND = #{expand}WHERE ID = #{id}]]></update><delete id="delete" parameterType="map"><![CDATA[delete from base_area]]><include refid="dynamicWhere"/></delete><delete id="batchDelete" parameterType="list">delete from base_area where ID IN <foreach collection="list" item="ids"  open="(" separator="," close=")">
#{ids}        </foreach> </delete><delete id="batchDeleteByLocalId" parameterType="list">delete from base_area where LOCAL_ID in<foreach collection="list" item="ids"  open="(" separator="," close=")">
#{ids}      </foreach> </delete><select id="getById" resultMap="baseAreaResult">SELECT <include refid="commonColumns" /><![CDATA[FROM base_area WHERE ID = #{id}]]></select><select id="getByIds" resultMap="baseAreaResult">SELECT <include refid="commonColumns" />FROM base_area WHERE ID IN <foreach collection="list" item="ids"  open="(" separator="," close=")">
#{ids}        </foreach>  </select><sql id="dynamicWhere"><!-- ognl访问静态方法的表达式 为@class@method(args),以下为调用rapid中的Ognl.isNotEmpty()方法,还有其它方法如isNotBlank()可以使用,具体请查看Ognl类 --><where>                      <if test="@Ognl@isNotEmpty(id)">AND ID = #{id}</if><if test="@Ognl@isNotEmpty(areaName)">AND AREA_NAME = #{areaName}</if><if test="@Ognl@isNotEmpty(areaCode)">AND AREA_CODE = #{areaCode}</if><if test="@Ognl@isNotEmpty(parentId)">AND PARENT_ID = #{parentId}</if><if test="@Ognl@isNotEmpty(platMark)">AND PLAT_MARK = #{platMark}</if><if test="@Ognl@isNotEmpty(level)">AND LEVEL = #{level}</if><if test="@Ognl@isNotEmpty(status)">AND STATUS = #{status}</if><if test="@Ognl@isNotEmpty(expand)">AND EXPAND = #{expand}</if></where></sql><select id="count" resultType="long">SELECT count(*) FROM base_area <include refid="dynamicWhere"/>    </select><!--分页查询已经使用Dialect进行分页,也可以不使用Dialect直接编写分页因为分页查询将传 offset,pageSize,lastRows 三个参数,不同的数据库可以根于此三个参数属性应用不同的分页实现--><select id="pageSelect" resultMap="baseAreaResult">SELECT <include refid="commonColumns" />FROM base_area <include refid="dynamicWhere"/><if test="@Ognl@isNotEmpty(sortColumns)">ORDER BY ${sortColumns}</if></select><select id="childrenSelect" resultMap="childrenResult">SELECT <include refid="commonColumns" />FROM base_area WHERE PARENT_ID = #{id}ORDER BY ID ASC</select></mapper>

Mapperxml 解析

1、主查询语句为: pageSelect

2、结果映射 resultMap id="baseAreaResult"

这里的关键在于:

<id property="id" column="ID"/>
<collection property="childrenList" javaType="java.util.ArrayList" column="id"ofType="com.wanyu.smarthome.model.BaseArea" select="childrenSelect"></collection>

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #4e9192}

collection 标签中定义属性名称为 childrenList,对应实体类中的:childrenList

属性类型为:java.util.ArrayList

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #932192} span.s1 {color: #000000} span.s2 {color: #3933ff}

column="id" 将 id 列的值做为参数传递给子查询

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #932192}

ofType 定义List 中保存的数据类型

select 定义子查询

3、注意子查询 childrenSelect 对应的结果映射 childrenResult ,又包含了 collection 标签,形成了循环递归调用

小结

不知道我有没有描述清楚,有不明白的地方请留言。

强烈不推荐这种循环递归调用的写法,因为性能非常差。

最好是有几级就写几级,也就是写几个 resultMap。

本粟中是演示同一个表相同实体类的实现方式,同样也可以不同的表不同的实体类,只要把子节点类型修改一下就可以了。

为了性能,这种查询最好配合缓存使用。

======================文档信息======================

版权声明:非商用自由转载-保持署名-注明出处

署名(BY) :testcs_dn(微wx笑)

文章出处:[无知人生,记录点滴](http://blog.csdn.NET/testcs_dn)

==============本文首发于个人微信订阅号(微wx笑)============

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Helvetica; color: #000000; -webkit-text-stroke: #000000} span.s1 {font-kerning: none} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #454545} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #3933ff} p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #4e9192}

springmvc+mybatis 无极限树形结构 Mapperxml 映射方法相关推荐

  1. springmvc+mybatis 无极限树形结构菜单

    MYSQL数据库 查出数据调用封装类 List<Menu> menulist = new ArrayList<Menu>();menulist = menuService.me ...

  2. Mybatis返回树形结构

    在返回json数据时常常需要返回树形的结构,自己去写递归来构建树的话又太麻烦了. 下面介绍一种使用mybatis来返回树形结构的好方法 表结构展示 表里面需要记录父级Id MyBatis一对多coll ...

  3. mybatis mysql 树形结构_Mybatis查询树形结构数据

    数据表的设计 创建数据表 对于树形结构的数据库设计通常是基于继承关系设计的,也就是通过父ID关联来实现的. 一张树形结构的数据表基本的三个字段:id(自己).name(名称)和parentid(父类编 ...

  4. 过滤树形结构数组的方法

    前天做项目时候,遇到一个方法,过滤树形数据,把符合条件的数据过滤出来,树形数据如下: const objarr= [{"id": 1,"isshow": tru ...

  5. 自定义类 无极限树形结构菜单(繁杂版)

    注意: 主要是理解形成树形菜单的思路,并不是粘贴就可以用的. 返回数据(如:效果图)到页面后,通过js做递归调用处理就好了. 树形菜单基础类: TreeGridCategory.java public ...

  6. 关于idea新建子目录时往父目录名字后叠加而不是树形结构的解决方法

    我们在IDEA中创建子目录时,子目录总是在父目录后面叠加而不是树形,如下: 我们可以打开项目窗口的右上角的设置标志: 将红圈选项的√先去掉,创建好子目录后再将它选中就可以

  7. mybatis 父子级树形结构查询

    针对父子级数据目录查询, 以前都是逐级的去根据父级id查询子集目录, 查出后最后再在代码中拼成树形结构, 相当复杂,我们可以利用 mybatis 提供的 collection 标签自动组织树形结构, ...

  8. 使用hibernate实现树形结构无限级分类

    转自 http://together.javaeye.com/blog/29482?page=2 在系统中,经常会用到无限级的树形结构分类,如组织机构管理.商品/地区分类等等.在以前的一个贴子:htt ...

  9. Java 平铺列表转为树形结构

    在业务中往往有一些数据是有层级结构的,比如数据表中原始数据如下形式 id name parentId 1 1 null 2 1-1 1 3 1-1-1 2 4 1-1 1 想要将平铺的数据变成有层次的 ...

  10. mybatis mysql查询树形结构_MyBatis collection 集合嵌套查询树形节点

    原标题:MyBatis collection 集合嵌套查询树形节点 MyBatis collection 集合 MyBatis 是数据持久层框架,支持定制化 SQL.存储过程以及高级映射.尤其强大在于 ...

最新文章

  1. Linux中断流程分析
  2. PHP扩展 Mongo 与 MongoDB
  3. jmeter constant timer 如何添加_基于jmeter+perfmon的稳定性测试记录
  4. python string/list转换
  5. linux 获取模块,get_module - 获取Linux内核模块的详细信息
  6. 为你的AliOS Things应用增加自定义cli命令
  7. 全面认识 RUST -- 掌控未来的雷电
  8. [Leetcode] Path Sum II路径和
  9. nyoj Color the fence
  10. 服务器的原理,服务器原理
  11. 冯雪 手术机器人的应用_智能手术机器人及其应用_谢俊祥.
  12. 激光振镜误差校正算法C语言,一种基于双线性插值法的激光振镜图形校正算法的制作方法...
  13. idea的简单使用,初始化过程
  14. 怎么写好一篇接口文档
  15. java面向对象三大特性之多态---编译时多态和运行时多态详解
  16. 安全月报| PeckShield:9月共发生安全事件14起,损失近1,800万美元
  17. NOIP 2016 天天爱跑步
  18. 全国计算机一级WORD第三套,全国计算机等级考试一级WPSOffice练习题及答案第三套.pdf...
  19. 计算机网络中常见的各层协议
  20. 服务器H110芯片组,技嘉(GIGABYTE) H110M-S2 主板 (Intel H110/LGA 1151)

热门文章

  1. quot;紫陀螺quot;网友感触转载系列之…
  2. ac多模式匹配 java_Aho-Corasick 多模式匹配算法、AC自动机详解
  3. Linux课程之linux的发展
  4. 【python 新浪微博爬虫】python 爬取新浪微博热门话题
  5. 计算机毕业设计之java+jsp517报刊图书征订管理系统
  6. lol服务器维护局,lol维护局 英雄联盟钻石维护局掉多少分
  7. 2023校招美团笔试
  8. Android Modem修改点以及修改方法
  9. 2010年8月初 泉州将地震 预言还是谣言?
  10. 浅聊智能推荐下的人物画像