MyBatis树形结构查询

前言

对于树形结构的数据库设计通常是基于继承关系设计的,也就是通过父ID关联来实现的,还有就是基于左右值编码设计。本文以继承关系设计的树形结构来讨论下MyBatis树形结构查询。以深度为二的树为例,要将这种结构的数据查询出来,通常的做法是先查询一级数据,再查询二级数据,然后在程序里组装起来,想了下,其实还可以更简单,不用分开两次,就能将不同级别数据一次性查询映射出来。

1.数据准备

以区域表为例,数据如下

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for area
-- ----------------------------
DROP TABLE IF EXISTS `area`;
CREATE TABLE `area` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'PK',
  `area_name` varchar(50) NOT NULL COMMENT '地区名称',
  `parent_id` int(11) NOT NULL COMMENT '父Id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7668 DEFAULT CHARSET=utf8 COMMENT='地区表';

-- ----------------------------
-- Records of area
-- ----------------------------
INSERT INTO `area` VALUES ('1', '广东省', '0');
INSERT INTO `area` VALUES ('2', '深圳市', '1');
INSERT INTO `area` VALUES ('3', '广州市', '1');
INSERT INTO `area` VALUES ('4', '湖南省', '0');
INSERT INTO `area` VALUES ('5', '长沙市', '4');
INSERT INTO `area` VALUES ('6', '株洲市', '4');

2.实体类

public class Area {
    private Integer id;

    private String areaName;

    private Integer parentId;

    List<Area> children;

   //getter setter
}

3.查询sql

SELECT
        t_top.id AS id1,
        t_top.area_name AS area_name1,
        t_top.parent_id AS parent_id1,
        t_second.id AS id2,
        t_second.area_name AS area_name2,
        t_second.parent_id AS parent_id2
    FROM
        (
            SELECT
                id,
                area_name,
                parent_id
            FROM
                area
            WHERE
                parent_id = 0
        ) AS t_top
    INNER JOIN (
        SELECT
            id,
            area_name,
            parent_id
        FROM
            area
        WHERE
            parent_id IN (
                SELECT
                    id
                FROM
                    area
                WHERE
                    parent_id = 0
            )
    ) AS t_second ON t_top.id = t_second.parent_id

基本思路就是将一级区域查询出来作为临时表t_top,将二级区域查询出来作为临时表t_second,然后将两张表做inner join操作,查询结果:

这里写图片描述

4.ResultMap映射

  <resultMap id="TreeResultMap" type="com.domain.Area" >
    <id column="id1" property="id" jdbcType="INTEGER" />
    <result column="area_name1" property="areaName" jdbcType="VARCHAR" />
    <result column="parent_id1" property="parentId" jdbcType="INTEGER" />
    <collection property="children" ofType="com.domain.Area">
      <id column="id2" property="id" jdbcType="INTEGER" />
      <result column="area_name2" property="areaName" jdbcType="VARCHAR" />
      <result column="parent_id2" property="parentId" jdbcType="INTEGER" />
    </collection>
  </resultMap>

单元测试结果:

这里写图片描述

MyBatis并没有提供树形结构查询的原生支持,但是可以通过自定义SQL语句来实现。下面是一种常见的实现方式: 1. 定义一个ResultMap,将查询结果映射成一个Java对象。 ```xml <resultMap id="treeNodeMap" type="TreeNode"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="parent_id" property="parentId" /> <collection property="children" ofType="TreeNode" resultMap="treeNodeMap" select="findChildren"/> </resultMap> ``` 2. 编写查询子节点的SQL语句,并在ResultMap中引用该语句。 ```xml <select id="findChildren" resultMap="treeNodeMap"> SELECT id, name, parent_id FROM tree_node WHERE parent_id = #{id} </select> ``` 3. 编写查询根节点的SQL语句,并在Mapper接口中定义对应的方法。 ```xml <select id="findRoots" resultMap="treeNodeMap"> SELECT id, name, parent_id FROM tree_node WHERE parent_id IS NULL </select> ``` ```java public interface TreeNodeMapper { List<TreeNode> findRoots(); } ``` 4. 在Service层调用findRoots方法,得到所有的根节点。遍历每个根节点,递归查询其所有子节点。 ```java public List<TreeNode> findTree() { List<TreeNode> roots = treeNodeMapper.findRoots(); for (TreeNode root : roots) { findChildren(root); } return roots; } private void findChildren(TreeNode node) { List<TreeNode> children = treeNodeMapper.findChildren(node.getId()); node.setChildren(children); for (TreeNode child : children) { findChildren(child); } } ``` 这样就可以得到一棵完整的树形结构。需要注意的是,如果树的层级比较深,递归查询可能会导致性能问题,可以考虑使用缓存或者优化SQL语句来解决。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值