MyBatis實現(xiàn)三級樹查詢的示例代碼
引言
在實際項目開發(fā)中,樹形結(jié)構(gòu)的數(shù)據(jù)查詢是一個非常常見的需求。比如組織架構(gòu)、菜單管理、地區(qū)選擇等場景都需要處理樹形數(shù)據(jù)。本文將詳細講解如何使用MyBatis實現(xiàn)三級樹形數(shù)據(jù)的查詢,從數(shù)據(jù)庫設計到具體代碼實現(xiàn),幫助大家掌握樹形數(shù)據(jù)處理的核心要點。
數(shù)據(jù)庫設計
首先,我們需要設計一個合適的數(shù)據(jù)庫表結(jié)構(gòu)來存儲樹形數(shù)據(jù)。以下是一個典型的樹形表結(jié)構(gòu):
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 '地區(qū)名稱', `level` int(11) NOT NULL COMMENT '層級(1:省份 2:城市 3:區(qū)縣)', `sort` int(11) DEFAULT 0 COMMENT '排序號', `status` tinyint(4) DEFAULT 1 COMMENT '狀態(tài)(0:禁用 1:啟用)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間', `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='地區(qū)表';
實體類設計
接下來,我們需要創(chuàng)建對應的實體類。為了支持樹形結(jié)構(gòu),我們需要添加一個children屬性來存儲子節(jié)點:
@Data public class Area implements Serializable { private static final long serialVersionUID = 1L; // 主鍵ID private Long id; // 父級ID private Long parentId; // 地區(qū)名稱 private String name; // 層級 private Integer level; // 排序號 private Integer sort; // 狀態(tài) private Integer status; // 創(chuàng)建時間 private Date createTime; // 更新時間 private Date updateTime; // 子節(jié)點列表 private List<Area> children; }
Mapper接口設計
創(chuàng)建AreaMapper接口,定義必要的查詢方法:
@Mapper public interface AreaMapper { /** * 查詢所有地區(qū)數(shù)據(jù) * @return 地區(qū)列表 */ List<Area> selectAllAreas(); /** * 根據(jù)父ID查詢子地區(qū) * @param parentId 父級ID * @return 子地區(qū)列表 */ List<Area> selectAreasByParentId(Long parentId); /** * 查詢指定層級的地區(qū) * @param level 層級 * @return 地區(qū)列表 */ List<Area> selectAreasByLevel(Integer level); }
XML映射文件實現(xiàn)
在resources目錄下創(chuàng)建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> <!-- 查詢所有地區(qū) --> <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> <!-- 根據(jù)父ID查詢子地區(qū) --> <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> <!-- 查詢指定層級的地區(qū) --> <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層實現(xiàn)
創(chuàng)建Service接口及其實現(xiàn)類:
@Service @Slf4j public class AreaServiceImpl implements AreaService { @Autowired private AreaMapper areaMapper; @Override public List<Area> buildAreaTree() { // 查詢所有地區(qū)數(shù)據(jù) List<Area> allAreas = areaMapper.selectAllAreas(); // 構(gòu)建樹形結(jié)構(gòu) return buildTree(allAreas); } /** * 構(gòu)建樹形結(jié)構(gòu) * @param areas 地區(qū)列表 * @return 樹形結(jié)構(gòu)的地區(qū)列表 */ private List<Area> buildTree(List<Area> areas) { List<Area> trees = new ArrayList<>(); // 獲取所有根節(jié)點(省份) areas.stream().filter(area -> area.getLevel() == 1) .forEach(province -> { // 設置省份的子節(jié)點(城市) List<Area> cities = getChildren(areas, province.getId()); province.setChildren(cities); // 設置城市的子節(jié)點(區(qū)縣) cities.forEach(city -> { List<Area> districts = getChildren(areas, city.getId()); city.setChildren(districts); }); trees.add(province); }); return trees; } /** * 獲取子節(jié)點 * @param areas 所有地區(qū)列表 * @param parentId 父級ID * @return 子節(jié)點列表 */ private List<Area> getChildren(List<Area> areas, Long parentId) { return areas.stream().filter(area -> Objects.equals(area.getParentId(), parentId)) .collect(Collectors.toList()); } }
Controller層實現(xiàn)
最后,創(chuàng)建Controller處理前端請求:
@RestController@RequestMapping("/api/areas") public class AreaController { @Autowired private AreaService areaService; /** * 獲取地區(qū)樹形數(shù)據(jù) */ @GetMapping("/tree") public ResponseResult<List<Area>> getAreaTree() { try { List<Area> trees = areaService.buildAreaTree(); return ResponseResult.success(trees); } catch (Exception e) { log.error("獲取地區(qū)樹形數(shù)據(jù)失敗", e); return ResponseResult.error("獲取地區(qū)樹形數(shù)據(jù)失敗"); } } }
性能優(yōu)化建議
- 緩存優(yōu)化
- 考慮使用Redis緩存樹形數(shù)據(jù),因為地區(qū)數(shù)據(jù)變動頻率較低
- 可以設置合理的緩存過期時間,如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; } // 緩存未命中,查詢數(shù)據(jù)庫并構(gòu)建樹 List<Area> trees = buildTreeFromDb(); // 放入緩存 redisTemplate.opsForValue().set(AREA_TREE_KEY, trees, CACHE_TIMEOUT, TimeUnit.HOURS); return trees; } }
2. SQL優(yōu)化
- 適當添加索引:parent_id, level, status等字段
- 考慮使用批量查詢替代循環(huán)查詢
3. 內(nèi)存優(yōu)化
- 使用Stream API時注意及時關(guān)閉
- 合理設置集合的初始容量
- 及時釋放不再使用的對象引用
總結(jié)
通過本文的講解,我們實現(xiàn)了一個完整的三級樹形數(shù)據(jù)查詢功能。關(guān)鍵要點包括:
- 合理的數(shù)據(jù)庫表設計,包含必要的字段和索引
- 清晰的實體類設計,支持樹形結(jié)構(gòu)
- MyBatis映射文件的編寫,實現(xiàn)基礎的數(shù)據(jù)查詢
- Service層的樹形構(gòu)建算法實現(xiàn)
- 性能優(yōu)化和緩存的使用
到此這篇關(guān)于MyBatis實現(xiàn)三級樹查詢的示例代碼的文章就介紹到這了,更多相關(guān)MyBatis三級樹查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot調(diào)用第三方接口的幾種方式小結(jié)
在項目中調(diào)用第三方接口時,確實需要根據(jù)項目的技術(shù)棧、架構(gòu)規(guī)范以及具體的業(yè)務需求來選擇最適合的調(diào)用方式,下面我們就介紹幾種調(diào)用第三方接口的實現(xiàn)方式以及代碼示例,需要的朋友可以參考下2024-07-07深入了解SparkSQL中數(shù)據(jù)的加載與保存
這篇文章主要為大家詳細介紹了SparkSQL中數(shù)據(jù)的加載與保存的相關(guān)知識,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解下2023-11-11java中int、double、char等變量的取值范圍詳析
這篇文章主要給大家介紹了關(guān)于java中int、double、char等變量取值范圍的相關(guān)資料,每個變量都給出了詳細的實例代碼,對大家學習或者使用java具有一定的參考學習價值,需要的朋友可以參考下2021-10-10java ThreadLocal線程局部變量常用方法使用場景示例詳解
這篇文章主要介紹了為大家java ThreadLocal線程局部變量常用方法使用場景示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07