当Mybatis遇上目录树,有哪些解决方法?

简介: 二级目录属于哪个一级目录?三级目录属于哪个二级目录?一个员工属于哪个上级员工?当Mybatis遇上目录树,有哪些解决方法?

相信你也遇到过这种场景,判断二级目录属于哪个一级目录,一个员工属于哪个上级员工领导...

Mybatis遇上目录树,有哪些解决方法?

一般来说,有xml直接实现和java代码递归赋值实现。

方式一:xml直接实现

这里列出category数据表数据

表结构如下

type表示分类类型,也就是目录级别,1表示一级目录,3表示三级目录

大家就不要关注数据类型规范了,比如这里id应该biginttype明明可以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_id1的子分类数据,也就是查询甜点/蛋糕分类下面的二级和三级分类,执行如下语句

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

结果如下

可以看到二级分类为蛋糕、点心时,有哪些对应的三级分类可以提供给前端,便于展示。

  我这里分为CategoryVOSubCategoryVO ,而不是把所有属性放在一个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>

首先让前端展示在首页的一级分类,前端调用一级分类接口,我们只需要查询type1的数据返回给前端,鼠标移动到一级分类,就调用获取子分类的接口,前端传入对应一级分类的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_id3的二级分类

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": []
        }
    ]
}



欢迎一键三连~



有问题请留言,大家一起探讨学习



----------------------Talk is cheap, show me the code-----------------------

目录
相关文章
|
Java 数据库连接 mybatis
mybatis返回map类型数据空值字段不显示(三种解决方法)
mybatis返回map类型数据空值字段不显示(三种解决方法)
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程
MyBatisPlus如何根据id批量查询?Required request parameter ‘id‘ for method 解决方法是看青戈大佬MybatisPlus的教程
|
4月前
|
Java 数据库连接 Maven
Private method ‘getVideoList()‘ is never used,mybatis必须指定Mapper文件和实体目录,在参考其他人写的代码,要认真分析别人的代码,不要丢失
Private method ‘getVideoList()‘ is never used,mybatis必须指定Mapper文件和实体目录,在参考其他人写的代码,要认真分析别人的代码,不要丢失
|
SQL Java 数据库
【Mybatis-plus异常】mybatis-plus updateById方法更新不了空字符串和null的解决方法
【Mybatis-plus异常】mybatis-plus updateById方法更新不了空字符串和null的解决方法
584 0
|
6月前
|
SQL Java 数据库连接
MyBatis【问题 02】使用#{}导致的问题(#{}并非简单地将传入数据前后添加单引号)及解决方法
MyBatis【问题 02】使用#{}导致的问题(#{}并非简单地将传入数据前后添加单引号)及解决方法
148 0
|
SQL Oracle Java
MyBatis-Plus 的妙用(总章节目录)
MyBatis-Plus 的妙用(总章节目录)
101 0
|
XML Java 数据库连接
Eclipse MyBatis1.4.2 generatorConfig 默认不生成XML文件,加了type="XMLMAPPER"也不起作用,解决方法
今天下载了最新的mybatis插件,生成不了XML文件,然后是一堆java注解文件,还有一堆报错。心头各种不爽,网上搜了很久,都是很旧的帖子,根据解决不了问题。最后自己在官网找到了答案,以后大家在搜索找不到答案,还是自己到官网翻文档吧! MyBatis 的官网generatorConfig说明如下: http://mybatis.org/generator/configreference/xmlconfig.html
464 0
Eclipse MyBatis1.4.2 generatorConfig 默认不生成XML文件,加了type="XMLMAPPER"也不起作用,解决方法
|
XML Java 数据库连接
【异常】Mybatis中,SpringMVC中,SpringBoot项目中,出现There is no getter for property named 'xxxxxx'报错的解决方法
Mybatis中,SpringMVC中,SpringBoot项目中,出现There is no getter for property named 'xxxxxx'报错的解决方法
397 0
|
SQL Java 数据库连接
使用MyBatis时,无法绑定抽象方法与配置SQL语句的异常解决方法
使用MyBatis时,无法绑定抽象方法与配置SQL语句的异常解决方法
173 0
|
SQL Java 数据库连接
mybatis学习笔记之学习目录(1)
mybatis学习笔记之学习结构(1) 学习结构: 1.mybatis开发方法 原始dao开发方法(程序需要编写dao接口和dao实现类) mybatis的mapper接口(相当于dao接口)代理开发方法 mybatis配置文件SqlMapConfig.xml mybatis核心: mybatis输入映射 mybatis输出映
1642 0