当Mybatis遇上目录树超全完美解决方案
作者:砖业洋__ 发布时间:2021-09-28 16:21:13
相信你也遇到过这种场景,判断二级目录属于哪个一级目录,一个员工属于哪个上级员工领导…
当Mybatis
遇上目录树,有哪些解决方法?
一般来说,有
xml
直接实现和java
代码递归赋值实现。
方式一:xml直接实现
这里列出category
数据表数据
表结构如下
type
表示分类类型,也就是目录级别,1表示一级目录,3表示 * 目录
大家就不要关注数据类型规范了,比如这里id
应该bigint
,type
明明可以tinyint
之类的,我抓我看到的例子直接讲解。
目录为甜点/蛋糕的id
为1,而蛋糕和点心的father_id
为1,目录为饼干/膨化的id
为2,饼干、薯片、虾条的father_id
就是2,一级目录id
对应二级子目录的father_id
,这就是所属对应关系,可以理解为父子关系。
实体类是mybatis-generator
插件自动生成的
public class Category {
private Integer id;
private String name;
private Integer type;
private Integer fatherId;
private String logo;
private String slogan;
private String catImage;
private String bgColor;
//=====篇幅原因,省掉Getter和Setter方法======
......
}
一般我们看到的商城,鼠标放到一级分类目录就会展示出二级分类目录。我们的需求是当鼠标移动到一级分类,我们需要提供二级分类和 * 分类。
这里贴出需要返回给前端的聚合模型view object
数据
/**
* 二级分类VO
*/
public class CategoryVO {
private Integer id;
private String name;
private String type;
private Integer fatherId;
// * 分类vo list
private List<SubCategoryVO> subCatList;
//=====篇幅原因,省掉Getter和Setter方法======
......
}
public class SubCategoryVO {
private Integer subId;
private String subName;
private String subType;
private Integer subFatherId;
//=====篇幅原因,省掉Getter和Setter方法======
......
}
这就涉及到自连接查询子目录的技巧了,我们试试查找father_id
是1
的子分类数据,也就是查询甜点/蛋糕分类下面的二级和 * 分类,执行如下语句
SELECT
f.id AS id,
f.`name` AS `name`,
f.type AS type,
f.father_id AS fatherId,
c.id AS subId,
c.`name` AS subName,
c.type AS subType,
c.father_id AS subFatherId
FROM
category f
LEFT JOIN category c ON f.id = c.father_id
WHERE
f.father_id = 1
结果如下
可以看到二级分类为蛋糕、点心时,有哪些对应的 * 分类可以提供给前端,便于展示。
我这里分为CategoryVO
、SubCategoryVO
,而不是把所有属性放在一个VO
,是为了便于理解。如果不用List
集合,而把所有属性放在一个VO
,前端收到的数据形式和你此时在数据库查询出来的一样,有多条蛋糕记录,底下对应着不同具体食品,这让前端不好处理也不符合逻辑,正常逻辑应该是只有一个蛋糕分类,然后这个分类里面有数组去装着蛋糕对应子分类才对。
这里其实只用一个CategoryVO
里面也可以处理,在后面第二种方式用java
代码处理多级目录时,你会看到我只用了一个CategoryVO
就能处理。
注意,二级分类的实体类CategoryVO
有个
private List<SubCategoryVO> subCatList;
这个subCatList
是为了存放 * 分类的vo list
,在xml
中 * 分类用了collection
对应这个list
<?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.me.mapper.CategoryMapperCustom" >
<resultMap id="myCategoryVO" type="com.me.pojo.vo.CategoryVO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="type" property="type"/>
<!--
column一定要在sql语句中找到,property一定要在对应实体类中找到
因为sql用as写了别名,所以column才能用fatherId,如果不用别名,还是得写father_id
-->
<result column="fatherId" property="fatherId"/>
<!--
collection 标签:用于定义关联的list集合类型的封装规则
property:对应 * 分类的list属性名
ofType:集合的类型, * 分类的vo
-->
<collection property="subCatList" ofType="com.me.pojo.vo.SubCategoryVO">
<id column="subId" property="subId"/>
<result column="subName" property="subName"/>
<result column="subType" property="subType"/>
<result column="subFatherId" property="subFatherId"/>
</collection>
</resultMap>
<select id="getSubCatList" resultMap="myCategoryVO" parameterType="int">
SELECT
f.id as id,
f.`name` as `name`,
f.type as type,
f.father_id as fatherId,
c.id as subId,
c.`name` as subName,
c.type as subType,
c.father_id as subFatherId
FROM
category f
LEFT JOIN
category c
on
f.id = c.father_id
WHERE
f.father_id = #{rootCatId}
</select>
</mapper>
首先让前端展示在首页的一级分类,前端调用一级分类接口,我们只需要查询type
为1
的数据返回给前端,鼠标移动到一级分类,就调用获取子分类的接口,前端传入对应一级分类的id
给后端,后端将这个id
作为father_id
去查询子分类。最后我们可以调用getSubCatList
来得到所有目录。
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<CategoryVO> getSubCatList(Integer rootCatId) {
return categoryMapperCustom.getSubCatList(rootCatId);
}
最后数据就是这样,如下
{
"status": 200,
"msg": "OK",
"data": [{
"id": 11,
"name": "蛋糕",
"type": "2", <==================type=2表示二级目录
"fatherId": 1,
"subCatList": [{
"subId": 37,
"subName": "蒸蛋糕",
"subType": "3", <================subType=3表示3级目录
"subFatherId": 11
}, {
"subId": 38,
"subName": "软面包",
"subType": "3",
"subFatherId": 11
}, {
"subId": 39,
"subName": "脱水蛋糕",
"subType": "3",
"subFatherId": 11
}, {
"subId": 40,
"subName": "马卡龙",
"subType": "3",
"subFatherId": 11
}, {
"subId": 41,
"subName": "甜甜圈",
"subType": "3",
"subFatherId": 11
}, {
"subId": 42,
"subName": "三明治",
"subType": "3",
"subFatherId": 11
}, {
"subId": 43,
"subName": "铜锣烧",
"subType": "3",
"subFatherId": 11
}]
}, {
"id": 12,
"name": "点心",
"type": "2",
"fatherId": 1,
"subCatList": [{
"subId": 44,
"subName": "肉松饼",
"subType": "3",
"subFatherId": 12
}, {
"subId": 45,
"subName": "华夫饼",
"subType": "3",
"subFatherId": 12
}, {
"subId": 46,
"subName": "沙琪玛",
"subType": "3",
"subFatherId": 12
}, {
"subId": 47,
"subName": "鸡蛋卷",
"subType": "3",
"subFatherId": 12
}, {
"subId": 48,
"subName": "蛋饼",
"subType": "3",
"subFatherId": 12
}, {
"subId": 49,
"subName": "凤梨酥",
"subType": "3",
"subFatherId": 12
}, {
"subId": 50,
"subName": "手撕面包",
"subType": "3",
"subFatherId": 12
}]
}]
}
方式二:java代码递归处理二级 * 目录
此刻我换一个数据库例子,但是还是和上面一个处理一级二级 * 分类的例子一样
数据表如下
表结构如下
和上一个例子大同小异,type
依然表示目录级别
此刻需要返回给前端的VO
如下,此刻我只写了一个CategoryVO
,没有写子VO
,可以对比前一种方式看看,道理都是一样的。
public class CategoryVO {
private Integer id;
private String name;
private Integer type;
private Integer parentId;
private Integer orderNum;
private Date createTime;
private Date updateTime;
private List<CategoryVO> childCategory = new ArrayList<>();
//=====篇幅原因,省掉Getter和Setter方法======
......
}
@Override
public List<CategoryVO> listCategoryForCustomer(Integer parentId) {
ArrayList<CategoryVO> categoryVOList = new ArrayList<>();
recursivelyFindCategories(categoryVOList, parentId);
return categoryVOList;
}
// 以该parentId对应的目录为根节点,查询下面所有子目录信息,categoryVOList是要返回给前端展示的聚合模型数据
private void recursivelyFindCategories(List<CategoryVO> categoryVOList, Integer parentId) {
// 递归获取所有子类别,并组合成为一个"目录树"
List<Category> list= categoryMapper.selectCategoriesByParentId(parentId); // 通过父id查询子分类
if (!CollectionUtils.isEmpty(list)) {
for (int i = 0; i < list.size(); ++i) {
Category category = list.get(i);
CategoryVO categoryVO = new CategoryVO();
BeanUtils.copyProperties(category, categoryVO);
categoryVOList.add(categoryVO);
// 这里当前目录id作为下一次的父id,查询有没有对应的子目录,getChildCategory()方法是返回定义的List<CategoryVO> childCategory
recursivelyFindCategories(categoryVO.getChildCategory(), categoryVO.getId());
}
}
}
XML
文件如下:
......
<resultMap id="BaseResultMap" type="com.me.mall.model.pojo.Category">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="type" jdbcType="INTEGER" property="type" />
<result column="parent_id" jdbcType="INTEGER" property="parentId" />
<result column="order_num" jdbcType="INTEGER" property="orderNum" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
id, `name`, `type`, parent_id, order_num, create_time, update_time
</sql>
<select id="selectCategoriesByParentId" parameterType="int" resultMap="BaseResultMap">
select <include refid="Base_Column_List"/>
from category
where parent_id = #{parentId}
</select>
......
我们手动查询模拟一下递归的过程,首先查询parent_id
为3
的二级分类
select *
from category
where parent_id = 3
结果递归查询的时候,又会发现parent_id=4
时还有数据,即还有 * 分类,我们手动查询试试
select *
from category
where parent_id = 4
示例数据如下:
{
"status": 10000,
"msg": "SUCCESS",
"data": [
{
"id": 4,
"name": "橘子橙子",
"type": 2, <=================代表二级目录
"parentId": 3,
"orderNum": 1,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2019-12-28T08:25:10.000+0000",
"childCategory": [ <===============代表还有 * 目录
{
"id": 19,
"name": "果冻橙",
"type": 3,
"parentId": 4,
"orderNum": 1,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2020-02-10T16:37:02.000+0000",
"childCategory": []
}
]
},
{
"id": 11,
"name": "草莓",
"type": 2,
"parentId": 3,
"orderNum": 2,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2019-12-28T07:44:42.000+0000",
"childCategory": []
},
{
"id": 12,
"name": "奇异果",
"type": 2,
"parentId": 3,
"orderNum": 3,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2019-12-28T08:25:12.000+0000",
"childCategory": []
},
{
"id": 14,
"name": "车厘子",
"type": 2,
"parentId": 3,
"orderNum": 4,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2019-12-28T08:25:12.000+0000",
"childCategory": []
},
{
"id": 28,
"name": "其他水果",
"type": 2,
"parentId": 3,
"orderNum": 4,
"createTime": "2019-12-17T17:17:00.000+0000",
"updateTime": "2019-12-28T08:25:12.000+0000",
"childCategory": []
}
]
}
来源:https://blog.csdn.net/qq_34115899/article/details/116033697


猜你喜欢
- 声明:下面的实例全部在linux下尝试,window下未尝试。有兴趣者可以试一下。文章针c初学者。c语言的强符号和弱符号是c初学者经常容易犯
- 首先我们发现现在我们所用的android智能手机大部分都有当你在打电话时按power键来挂断电话,一般都是在设置中。 我主要是在原生源码中添
- 本文实例为大家分享了Java实现石头剪刀布的具体代码,供大家参考,具体内容如下代码:package com.neusoft.test;imp
- 本文实例为大家分享了viewGroup实现点击动画效果展示的具体代码,供大家参考,具体内容如下public class MyCustomVi
- 前言我们在使用spring security的时候可以通过好几种方法获取用户信息, 但是今天这篇文章介绍的是一个笔者觉得最优雅的实现; 借鉴
- 本人是用易语言起步的,起初是为了兴趣,后来由于易语言被杀软误杀严重,连空白程序都杀,后来转到了学C#,随着学习的深入,接触越来越复杂的东西之
- 有时候会不可避免使用动态表或者列进行业务处理。下面学习几种动态表/列的使用方式:【1】使用预编译即,默认值。<select id=&q
- 补间可以实现两个图形之间颜色、形状、大小、位置等的线性变化。例如A...AB...BC...C,其中A、B、C是三幅图片,两个A的宽分别是1
- 看到很多人解析歌词文件时写了一大片的字符处理代码,而且看得不是很明白,所以自己研究了一下, 首先来了解下Lrc文件 时间
- Java持久化框架:Hibernate与MybatisHibernate和Mybatis是两个Java持久化框架,用于将Java对象映射到关
- 本文实例讲述了C#实现汉字转拼音或转拼音首字母的方法。分享给大家供大家参考。具体实现方法如下:/// <summary>///
- 前言在计算机操作系统中,进程是进行资源分配和调度的基本单位。这对于基于Linux内核的Android系统也不例外。在Android的设计中,
- 最可怕的不是犯错而是一直都没发现错误,直到现在我才知道自己对类变量的理解有问题。大概可能也许是因为不常用类变量的原因吧,一直没有发现这个问题
- 存储的可选项Android 的文件系统类似于其他平台的基于磁盘的文件系统。包括以下几种存储类别:App 私有的存储空间共享的存储空间Pref
- 本文实例讲述了android通过Location API显示地址信息的实现方法。分享给大家供大家参考。具体如下:android的Locati
- clone()和Cloneable接口clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址。我们知道拿到一个对象的地址,只
- 前言说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少。有不少人
- 何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的事物却是我们经常使用到的。只要我们的函
- 多线程@Async的使用体验场景导入:可以将大批量的数据insert操作采用多线程的方式并行执行第三方服务的接口调用:由于存在个别第三方服务
- 本文实例为大家分享了Android实现轮播图片效果的具体代码,供大家参考,具体内容如下一、原理首先,将这些要轮播的图片和一些文本分别放置在不