MyBatis實(shí)現(xiàn)三級(jí)樹(shù)查詢(xún)的示例代碼
引言
在實(shí)際項(xiàng)目開(kāi)發(fā)中,樹(shù)形結(jié)構(gòu)的數(shù)據(jù)查詢(xún)是一個(gè)非常常見(jiàn)的需求。比如組織架構(gòu)、菜單管理、地區(qū)選擇等場(chǎng)景都需要處理樹(shù)形數(shù)據(jù)。本文將詳細(xì)講解如何使用MyBatis實(shí)現(xiàn)三級(jí)樹(shù)形數(shù)據(jù)的查詢(xún),從數(shù)據(jù)庫(kù)設(shè)計(jì)到具體代碼實(shí)現(xiàn),幫助大家掌握樹(shù)形數(shù)據(jù)處理的核心要點(diǎn)。
數(shù)據(jù)庫(kù)設(shè)計(jì)
首先,我們需要設(shè)計(jì)一個(gè)合適的數(shù)據(jù)庫(kù)表結(jié)構(gòu)來(lái)存儲(chǔ)樹(shù)形數(shù)據(jù)。以下是一個(gè)典型的樹(shù)形表結(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ū)名稱(chēng)', `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í)體類(lèi)設(shè)計(jì)
接下來(lái),我們需要?jiǎng)?chuàng)建對(duì)應(yīng)的實(shí)體類(lèi)。為了支持樹(shù)形結(jié)構(gòu),我們需要添加一個(gè)children屬性來(lái)存儲(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ū)名稱(chēng)
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接口,定義必要的查詢(xún)方法:
@Mapper
public interface AreaMapper {
/**
* 查詢(xún)所有地區(qū)數(shù)據(jù)
* @return 地區(qū)列表 */
List<Area> selectAllAreas();
/**
* 根據(jù)父ID查詢(xún)子地區(qū)
* @param parentId 父級(jí)ID
* @return 子地區(qū)列表 */
List<Area> selectAreasByParentId(Long parentId);
/**
* 查詢(xún)指定層級(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>
<!-- 查詢(xún)所有地區(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查詢(xún)子地區(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>
<!-- 查詢(xún)指定層級(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)類(lèi):
@Service
@Slf4j
public class AreaServiceImpl implements AreaService {
@Autowired
private AreaMapper areaMapper;
@Override
public List<Area> buildAreaTree() {
// 查詢(xún)所有地區(qū)數(shù)據(jù)
List<Area> allAreas = areaMapper.selectAllAreas();
// 構(gòu)建樹(shù)形結(jié)構(gòu)
return buildTree(allAreas);
}
/**
* 構(gòu)建樹(shù)形結(jié)構(gòu)
* @param areas 地區(qū)列表
* @return 樹(shù)形結(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ù)形數(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ù)形數(shù)據(jù)失敗", e);
return ResponseResult.error("獲取地區(qū)樹(shù)形數(shù)據(jù)失敗");
}
}
}
性能優(yōu)化建議
- 緩存優(yōu)化
- 考慮使用Redis緩存樹(shù)形數(shù)據(jù),因?yàn)榈貐^(qū)數(shù)據(jù)變動(dòng)頻率較低
- 可以設(shè)置合理的緩存過(guò)期時(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;
}
// 緩存未命中,查詢(xún)數(shù)據(jù)庫(kù)并構(gòu)建樹(shù)
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等字段
- 考慮使用批量查詢(xún)替代循環(huán)查詢(xún)
3. 內(nèi)存優(yōu)化
- 使用Stream API時(shí)注意及時(shí)關(guān)閉
- 合理設(shè)置集合的初始容量
- 及時(shí)釋放不再使用的對(duì)象引用
總結(jié)
通過(guò)本文的講解,我們實(shí)現(xiàn)了一個(gè)完整的三級(jí)樹(shù)形數(shù)據(jù)查詢(xún)功能。關(guān)鍵要點(diǎn)包括:
- 合理的數(shù)據(jù)庫(kù)表設(shè)計(jì),包含必要的字段和索引
- 清晰的實(shí)體類(lèi)設(shè)計(jì),支持樹(shù)形結(jié)構(gòu)
- MyBatis映射文件的編寫(xiě),實(shí)現(xiàn)基礎(chǔ)的數(shù)據(jù)查詢(xún)
- Service層的樹(shù)形構(gòu)建算法實(shí)現(xiàn)
- 性能優(yōu)化和緩存的使用
到此這篇關(guān)于MyBatis實(shí)現(xiàn)三級(jí)樹(shù)查詢(xún)的示例代碼的文章就介紹到這了,更多相關(guān)MyBatis三級(jí)樹(shù)查詢(xún)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法
本篇文章主要介紹了spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
關(guān)于JDK15的新特性之TextBlocks文本塊的引入和使用
這篇文章主要介紹了關(guān)于JDK15的新特性之文本塊的引入和使用,如果具有一種語(yǔ)言學(xué)機(jī)制,可以比多行文字更直觀地表示字符串,而且可以跨越多行,而且不會(huì)出現(xiàn)轉(zhuǎn)義的視覺(jué)混亂,那么這將提高廣泛Java類(lèi)程序的可讀性和可寫(xiě)性,需要的朋友可以參考下2023-07-07
淺析Java如何高效將PDF轉(zhuǎn)換為高質(zhì)量TIFF圖片
在文檔處理和歸檔系統(tǒng)中,將PDF文件轉(zhuǎn)換為T(mén)IFF格式是一項(xiàng)非常常見(jiàn)的需求,本文將介紹如何使用Java通過(guò)Spire.PDF?for?Java庫(kù),快速實(shí)現(xiàn)PDF到TIFF的轉(zhuǎn)換,希望對(duì)大家有所幫助2026-04-04
實(shí)例講解spring boot 多線(xiàn)程
這篇文章主要介紹了spring boot 多線(xiàn)程的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
Mybatis-Plus select不去查全部字段和去重問(wèn)題
本文介紹了MyBatis-Plus中查詢(xún)指定字段的方法和查詢(xún)分組查詢(xún)的方法,還介紹了使用`queryWrapper`的的`select()`方法來(lái)指定查詢(xún)的字段,并并并并提供了查詢(xún)?nèi)ブ?IN操作和特殊字符轉(zhuǎn)義的示例代碼2026-04-04
SpringBoot多種環(huán)境自由切換的實(shí)現(xiàn)
本文主要介紹了SpringBoot多種環(huán)境自由切換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
SpringBoot項(xiàng)目中Date類(lèi)型數(shù)據(jù)在接口返回的時(shí)間不正確的問(wèn)題解決
如果接口返回的Date類(lèi)型時(shí)間與數(shù)據(jù)庫(kù)中datetime不一致,可能是由于沒(méi)有正確配置時(shí)區(qū)導(dǎo)致的,解決方法是在yaml配置文件中指定正確的日期格式和時(shí)區(qū)配置,修改配置并重啟項(xiàng)目后,可以獲得正確的時(shí)間,下面就來(lái)介紹一下2024-09-09
在Spring Boot項(xiàng)目中引入本地JAR包的步驟和配置
本文探討了在Spring Boot項(xiàng)目中引入本地JAR包的步驟和必要的配置,通過(guò)使用Maven的system作用域,開(kāi)發(fā)者可以將自定義的本地庫(kù)或功能集成到Spring Boot應(yīng)用程序中,,需要的朋友可以參考下2023-10-10

