在后台管理系统开发中,菜单管理是一个核心功能,而删除菜单作为其中的重要操作,需要考虑数据完整性、关联关系处理和操作安全性。本文将详细介绍 Spring Boot 环境下菜单删除功能的实现逻辑,包括关联数据处理、事务控制和异常处理等关键环节。
菜单删除的业务逻辑分析
菜单删除并非简单地删除一条记录,需要考虑以下几点:
- 菜单可能存在多级嵌套关系(父菜单与子菜单)
- 菜单与角色存在多对多关联(通过中间表 role_menu)
- 操作需要具备原子性(要么全部成功,要么全部失败)
因此,完整的菜单删除逻辑应该是:先删除关联数据,再删除子菜单,最后删除父菜单本身,并通过事务保证整个过程的完整性。
实现代码与步骤解析
1. 控制器层(MenuController)
首先定义删除接口,接收菜单 ID 并添加事务注解:
@RestController
@RequestMapping("/menu")
public class MenuController {
@Autowired
private MenuService menuService;
@Autowired
private RoleMenuMapper roleMenuMapper;
/**
* 删除菜单
* @param id 菜单ID
* @return 操作结果
*/
@DeleteMapping("/del/{id}")
@Transactional // 事务注解,确保操作原子性
public Result deleteMenu(@PathVariable Integer id) {
try {
// 1. 查询所有子菜单
QueryWrapper<MenuEntity> subMenuQuery = new QueryWrapper<>();
subMenuQuery.eq("parent_id", id);
List<MenuEntity> subMenus = menuService.list(subMenuQuery);
// 2. 先删除子菜单与角色的关联关系
if (!subMenus.isEmpty()) {
for (MenuEntity subMenu : subMenus) {
QueryWrapper<RoleMenuEntity> roleMenuQuery = new QueryWrapper<>();
roleMenuQuery.eq("menu_id", subMenu.getMenuId());
roleMenuMapper.delete(roleMenuQuery);
}
// 3. 删除子菜单
menuService.remove(subMenuQuery);
}
// 4. 删除当前菜单与角色的关联关系
QueryWrapper<RoleMenuEntity> parentRoleMenuQuery = new QueryWrapper<>();
parentRoleMenuQuery.eq("menu_id", id);
roleMenuMapper.delete(parentRoleMenuQuery);
// 5. 删除当前菜单
boolean isDeleted = menuService.removeById(id);
if (isDeleted) {
return Result.success("菜单删除成功");
} else {
return Result.error("菜单不存在或已被删除");
}
} catch (Exception e) {
// 抛出运行时异常,触发事务回滚
throw new RuntimeException("删除菜单失败:" + e.getMessage());
}
}
}
2. 关键步骤解析
步骤 1:查询子菜单
通过 parent_id 查询当前菜单的所有子菜单,为后续级联删除做准备:
QueryWrapper<MenuEntity> subMenuQuery = new QueryWrapper<>();
subMenuQuery.eq("parent_id", id);
List<MenuEntity> subMenus = menuService.list(subMenuQuery);
步骤 2:删除子菜单与角色的关联关系
由于菜单与角色通过中间表 role_menu 关联,需要先删除这些关联数据,避免外键约束错误:
for (MenuEntity subMenu : subMenus) {
QueryWrapper<RoleMenuEntity> roleMenuQuery = new QueryWrapper<>();
roleMenuQuery.eq("menu_id", subMenu.getMenuId());
roleMenuMapper.delete(roleMenuQuery);
}
步骤 3:删除子菜单
在删除关联数据后,批量删除所有子菜单:
menuService.remove(subMenuQuery);
步骤 4:删除当前菜单与角色的关联关系
同样需要解除当前菜单与角色的关联:
QueryWrapper<RoleMenuEntity> parentRoleMenuQuery = new QueryWrapper<>();
parentRoleMenuQuery.eq("menu_id", id);
roleMenuMapper.delete(parentRoleMenuQuery);
步骤 5:删除当前菜单
最后删除主菜单记录:
menuService.removeById(id);
事务管理与异常处理
事务保证:通过
@Transactional
注解,确保整个删除过程的原子性。如果任何步骤失败,所有操作都会回滚,避免数据不一致。异常处理:
- 使用 try-catch 块捕获可能的异常
- 抛出
RuntimeException
触发事务回滚(符合 Spring 事务默认回滚规则) - 提供清晰的错误信息,便于问题排查
删除顺序原则:
- 先删关联数据,再删主数据
- 先删子菜单,再删父菜单
- 遵循 "从外向内、从下到上" 的删除顺序,避免外键约束冲突