MyBatis实现三级树查询的示例代码

 更新时间:2024年12月03日 09:13:23   作者:trymoLiu  
在实际项目开发中,树形结构的数据查询是一个非常常见的需求,比如组织架构、菜单管理、地区选择等场景都需要处理树形数据,本文将详细讲解如何使用MyBatis实现三级树形数据的查询,需要的朋友可以参考下

引言

在实际项目开发中,树形结构的数据查询是一个非常常见的需求。比如组织架构、菜单管理、地区选择等场景都需要处理树形数据。本文将详细讲解如何使用MyBatis实现三级树形数据的查询,从数据库设计到具体代码实现,帮助大家掌握树形数据处理的核心要点。

数据库设计

首先,我们需要设计一个合适的数据库表结构来存储树形数据。以下是一个典型的树形表结构:

CREATE TABLE `sys_area` 
(  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID', 
`name` varchar(50) NOT NULL COMMENT '地区名称',
`level` int(11) NOT NULL COMMENT '层级(1:省份 2:城市 3:区县)',
`sort` int(11) DEFAULT 0 COMMENT '排序号',  
`status` tinyint(4) DEFAULT 1 COMMENT '状态(0:禁用 1:启用)',  
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 
PRIMARY KEY (`id`),  
KEY `idx_parent_id` (`parent_id`)) 
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='地区表';

实体类设计

接下来,我们需要创建对应的实体类。为了支持树形结构,我们需要添加一个children属性来存储子节点:

@Data
public class Area implements Serializable {
      private static final long serialVersionUID = 1L;
          // 主键ID    
          private Long id;
          // 父级ID
          private Long parentId;
          // 地区名称
          private String name;
          // 层级   
          private Integer level; 
          // 排序号   
          private Integer sort;   
          // 状态   
          private Integer status;
          // 创建时间   
          private Date createTime;
          // 更新时间    
          private Date updateTime;  
          // 子节点列表   
          private List<Area> children;
      }

Mapper接口设计

创建AreaMapper接口,定义必要的查询方法:

@Mapper
public interface AreaMapper {
    /**     
    * 查询所有地区数据    
    * @return 地区列表     */  
    List<Area> selectAllAreas(); 
    /**     
    * 根据父ID查询子地区     
    * @param parentId 父级ID     
    * @return 子地区列表     */   
    List<Area> selectAreasByParentId(Long parentId);        
    /**    
    * 查询指定层级的地区     
    * @param level 层级    
    * @return 地区列表     */  
    List<Area> selectAreasByLevel(Integer level);
}

XML映射文件实现

在resources目录下创建AreaMapper.xml文件:

<?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.example.mapper.AreaMapper">
    <!-- 基础列 -->  
    <sql id="Base_Column_List"> 
        id, parent_id, name, level, sort, status, create_time, update_time   
    </sql>       
    <!-- 查询所有地区 --> 
    <select id="selectAllAreas" resultType="com.example.entity.Area"> 
        SELECT   <include refid="Base_Column_List"/>    FROM sys_area 
        WHERE status = 1      
        ORDER BY sort ASC, id ASC   
    </select>       
    <!-- 根据父ID查询子地区 -->  
    <select id="selectAreasByParentId" resultType="com.example.entity.Area"> 
        SELECT     <include refid="Base_Column_List"/>     FROM sys_area        
        WHERE status = 1        
        AND parent_id = #{parentId}       
        ORDER BY sort ASC, id ASC   
    </select>      
    <!-- 查询指定层级的地区 -->   
    <select id="selectAreasByLevel" resultType="com.example.entity.Area">  
        SELECT     <include refid="Base_Column_List"/>    FROM sys_area        
        WHERE status = 1     
        AND level = #{level}    
        ORDER BY sort ASC, id ASC  
    </select>
</mapper>

Service层实现

创建Service接口及其实现类:

@Service
@Slf4j
public class AreaServiceImpl implements AreaService {
    @Autowired
    private AreaMapper areaMapper; 
    @Override
    public List<Area> buildAreaTree() { 
        // 查询所有地区数据 
        List<Area> allAreas = areaMapper.selectAllAreas(); 
        // 构建树形结构
        return buildTree(allAreas);
    } 
    /**
    * 构建树形结构 
    * @param areas 地区列表 
    * @return 树形结构的地区列表 
    */ 
    private List<Area> buildTree(List<Area> areas) { 
        List<Area> trees = new ArrayList<>(); 
        // 获取所有根节点(省份)
        areas.stream().filter(area -> area.getLevel() == 1) .forEach(province -> { 
            // 设置省份的子节点(城市) 
            List<Area> cities = getChildren(areas, province.getId()); 
            province.setChildren(cities); 
            // 设置城市的子节点(区县)
            cities.forEach(city -> { List<Area> districts = getChildren(areas, city.getId());   
                city.setChildren(districts);  
            });
            trees.add(province);
        });
        return trees; 
    } 
    /**
    * 获取子节点 
    * @param areas 所有地区列表 
    * @param parentId 父级ID 
    * @return 子节点列表
    */ 
    private List<Area> getChildren(List<Area> areas, Long parentId) { 
        return areas.stream().filter(area -> Objects.equals(area.getParentId(), parentId)) 
        .collect(Collectors.toList()); 
    }
}

Controller层实现

最后,创建Controller处理前端请求:

@RestController@RequestMapping("/api/areas")
public class AreaController {   
    @Autowired   
    private AreaService areaService;    
    /**    
    * 获取地区树形数据     */ 
    @GetMapping("/tree")   
    public ResponseResult<List<Area>> getAreaTree() { 
        try {       
            List<Area> trees = areaService.buildAreaTree();   
            return ResponseResult.success(trees);    
        } catch (Exception e) {   
            log.error("获取地区树形数据失败", e);    
            return ResponseResult.error("获取地区树形数据失败");     
        }  
   }
}

性能优化建议

  • 缓存优化
  • 考虑使用Redis缓存树形数据,因为地区数据变动频率较低
  • 可以设置合理的缓存过期时间,如24小时
@Service
public class AreaServiceImpl implements AreaService {
    @Autowired   
    private RedisTemplate<String, List<Area>> redisTemplate; 
    private static final String AREA_TREE_KEY = "AREA:TREE";  
    private static final long CACHE_TIMEOUT = 24; 
    // 小时   
    @Override   
    public List<Area> buildAreaTree() {
        // 先从缓存获取  
        List<Area> cacheTree = redisTemplate.opsForValue().get(AREA_TREE_KEY);   
        if (cacheTree != null) {   
            return cacheTree;  
        }               
        // 缓存未命中,查询数据库并构建树    
        List<Area> trees = buildTreeFromDb();       
        // 放入缓存    
        redisTemplate.opsForValue().set(AREA_TREE_KEY, trees, CACHE_TIMEOUT, TimeUnit.HOURS);                return trees;
    }
}

2. SQL优化

  • 适当添加索引:parent_id, level, status等字段
  • 考虑使用批量查询替代循环查询

3. 内存优化

  • 使用Stream API时注意及时关闭
  • 合理设置集合的初始容量
  • 及时释放不再使用的对象引用

总结

通过本文的讲解,我们实现了一个完整的三级树形数据查询功能。关键要点包括:

  • 合理的数据库表设计,包含必要的字段和索引
  • 清晰的实体类设计,支持树形结构
  • MyBatis映射文件的编写,实现基础的数据查询
  • Service层的树形构建算法实现
  • 性能优化和缓存的使用

到此这篇关于MyBatis实现三级树查询的示例代码的文章就介绍到这了,更多相关MyBatis三级树查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring boot整合redis实现shiro的分布式session共享的方法

    spring boot整合redis实现shiro的分布式session共享的方法

    本篇文章主要介绍了spring boot整合redis实现shiro的分布式session共享的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 关于JDK15的新特性之TextBlocks文本块的引入和使用

    关于JDK15的新特性之TextBlocks文本块的引入和使用

    这篇文章主要介绍了关于JDK15的新特性之文本块的引入和使用,如果具有一种语言学机制,可以比多行文字更直观地表示字符串,而且可以跨越多行,而且不会出现转义的视觉混乱,那么这将提高广泛Java类程序的可读性和可写性,需要的朋友可以参考下
    2023-07-07
  • 浅谈spring-kafka消费异常处理

    浅谈spring-kafka消费异常处理

    本文主要介绍了spring-kafka消费异常处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-09-09
  • 浅析Java如何高效将PDF转换为高质量TIFF图片

    浅析Java如何高效将PDF转换为高质量TIFF图片

    在文档处理和归档系统中,将PDF文件转换为TIFF格式是一项非常常见的需求,本文将介绍如何使用Java通过Spire.PDF for Java库,快速实现PDF到TIFF的转换,希望对大家有所帮助
    2026-04-04
  • 实例讲解spring boot 多线程

    实例讲解spring boot 多线程

    这篇文章主要介绍了spring boot 多线程的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • Mybatis-Plus select不去查全部字段和去重问题

    Mybatis-Plus select不去查全部字段和去重问题

    本文介绍了MyBatis-Plus中查询指定字段的方法和查询分组查询的方法,还介绍了使用`queryWrapper`的的`select()`方法来指定查询的字段,并并并并提供了查询去重;IN操作和特殊字符转义的示例代码
    2026-04-04
  • SpringBoot多种环境自由切换的实现

    SpringBoot多种环境自由切换的实现

    本文主要介绍了SpringBoot多种环境自由切换的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • SpringBoot项目中Date类型数据在接口返回的时间不正确的问题解决

    SpringBoot项目中Date类型数据在接口返回的时间不正确的问题解决

    如果接口返回的Date类型时间与数据库中datetime不一致,可能是由于没有正确配置时区导致的,解决方法是在yaml配置文件中指定正确的日期格式和时区配置,修改配置并重启项目后,可以获得正确的时间,下面就来介绍一下
    2024-09-09
  • 在Spring Boot项目中引入本地JAR包的步骤和配置

    在Spring Boot项目中引入本地JAR包的步骤和配置

    本文探讨了在Spring Boot项目中引入本地JAR包的步骤和必要的配置,通过使用Maven的system作用域,开发者可以将自定义的本地库或功能集成到Spring Boot应用程序中,,需要的朋友可以参考下
    2023-10-10
  • Java反射机制详解

    Java反射机制详解

    Java的反射机制是在运行状态中,对于任何一个类,都可以知道这个类的所有属性和方法,对于任何一个对象,都可以调用它所有的方法和属性,修改部分类型信息,这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制
    2022-09-09

最新评论