文章目录
- 引言
- 数据库设计
- 实体类设计
- Mapper接口设计
- XML映射文件实现
- Service层实现
- Controller层实现
- 性能优化建议
- 1、缓存优化
- 2.SQL优化
- 3.内存优化
- 总结
引言
在实际项目开发中,树形结构的数据查询是一个非常常见的需求。比如组织架构、菜单管理、地区选择等场景都需要处理树形数据。本文将详细讲解如何使用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("获取地区树形数据失败");
}
}
}
性能优化建议
1、缓存优化
- 考虑使用
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
层的树形构建算法实现- 性能优化和缓存的使用