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 session(redis存储方式)监听导致创建大量redisMessageListenerConta
这篇文章主要介绍了解决Spring session(redis存储方式)监听导致创建大量redisMessageListenerContailner-X线程问题,需要的朋友可以参考下2018-08-08
最新评论