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