Spring Boot MongoDB获取三级子菜单遇空指针异常求解决
问题描述
在Spring Boot集成MongoDB环境中,需根据ID查询三级嵌套的子菜单数据,但多次尝试均失败。
数据结构示例
{ "title": "box", "active": false, "level": "you", "children": [ { "id": 1, "title": "hood", "type": "uod", "active": false, "level": "9", "children": [ { "id":1, "title": "New", "type": "hog", "active": false, "level": "3" } ] } ] }
编写的API代码
@GetMapping("/menus/{id}/submenu/{subId}/children/{childId}") public List<?> findChildrenById(@PathVariable("id") long id, @PathVariable("subId") long subId, @PathVariable("childId") long childId) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(id).and("children.id").is(subId).and("children.children.id").is(childId)); return operations.findOne(query, SubMenu.class).getChildren(); }
错误信息
java.lang.NullPointerException: Cannot invoke "com.menu.entity.SubMenu.getChildren()" because the return value of "org.springframework.data.mongodb.core.MongoOperations.findOne(org.springframework.data.mongodb.core.query.Query, java.lang.Class)" is null at com..menu.controller.MenuItemsGetControlller.findChildrenById(MenuItemsGetControlller.java:84)
解决方案
1. 先处理空指针异常
findOne返回null说明无匹配文档,直接调用getChildren()必然触发空指针,必须先做非空判断:
@GetMapping("/menus/{id}/submenu/{subId}/children/{childId}") public List<?> findChildrenById(@PathVariable("id") long id, @PathVariable("subId") long subId, @PathVariable("childId") long childId) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(id) .and("children.id").is(subId) .and("children.children.id").is(childId)); SubMenu menu = operations.findOne(query, SubMenu.class); if (menu == null) { // 返回空列表或自定义未找到响应 return Collections.emptyList(); // 也可抛出异常:throw new ResponseStatusException(HttpStatus.NOT_FOUND, "目标菜单不存在"); } // 后续处理三级子菜单逻辑 }
2. 修正查询逻辑,精准定位三级子菜单
原查询仅匹配存在对应ID的文档,但不会过滤嵌套数组元素,且返回的是二级菜单而非目标三级菜单。需通过投影或聚合查询来精准获取目标数据:
方式一:使用投影过滤数组
@GetMapping("/menus/{id}/submenu/{subId}/children/{childId}") public List<?> findChildrenById(@PathVariable("id") long id, @PathVariable("subId") long subId, @PathVariable("childId") long childId) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(id) .and("children.id").is(subId) .and("children.children.id").is(childId)); // 投影只返回匹配的三级子菜单 query.fields() .include("children.$.children.$") .exclude("_id"); Map<String, Object> result = operations.findOne(query, Map.class); if (result == null) { return Collections.emptyList(); } // 提取三级子菜单 List<Map<String, Object>> secondLevel = (List<Map<String, Object>>) result.get("children"); if (secondLevel == null || secondLevel.isEmpty()) { return Collections.emptyList(); } List<Map<String, Object>> thirdLevel = (List<Map<String, Object>>) secondLevel.get(0).get("children"); return thirdLevel != null ? thirdLevel : Collections.emptyList(); }
方式二:使用Aggregation聚合查询(更灵活)
适合复杂嵌套结构的查询:
@GetMapping("/menus/{id}/submenu/{subId}/children/{childId}") public List<?> findChildrenById(@PathVariable("id") long id, @PathVariable("subId") long subId, @PathVariable("childId") long childId) { Aggregation aggregation = Aggregation.newAggregation( Aggregation.match(Criteria.where("id").is(id)), Aggregation.unwind("children"), Aggregation.match(Criteria.where("children.id").is(subId)), Aggregation.unwind("children.children"), Aggregation.match(Criteria.where("children.children.id").is(childId)), Aggregation.project("children.children").andExclude("_id") ); AggregationResults<Map> results = operations.aggregate(aggregation, "your_collection_name", Map.class); List<Map> mappedResults = results.getMappedResults(); List<Object> thirdLevelMenus = new ArrayList<>(); for (Map map : mappedResults) { thirdLevelMenus.add(map.get("children")); } return thirdLevelMenus.isEmpty() ? Collections.emptyList() : thirdLevelMenus; }
3. 检查实体类匹配
确保SubMenu实体类与MongoDB文档结构完全匹配:
- 根菜单
id字段类型需与数据库一致(若数据库是int或字符串,需同步修改实体类类型) children字段需用List<SubMenu>映射嵌套结构,示例如下:
public class SubMenu { private Long id; private String title; private boolean active; private String level; private String type; private List<SubMenu> children; // Getters & Setters }
内容的提问来源于stack exchange,提问作者tauheed Alzohar
相关产品推荐
相关产品推荐

