// 删除处理 const handleDelete = async (id) => { try { await ElMessageBox.confirm(‘确认删除该分类及其所有子分类?’, ‘警告’, { type: ‘warning’, confirmButtonText: ‘确认’, cancelButtonText: ‘取消’ }) const res = await request.delete(/category/deleteById/${id}) if (res.code === ‘200’) { ElMessage.success(‘删除成功’) await loadData() } } catch (error) { if (error !== ‘cancel’) { console.error(‘删除失败:’, error) ElMessage.error(error.response?.data?.message || ‘删除失败’) } } }已经知道前端删除的代码如上所展示,数据库表代码为CREATE TABLE category ( id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘课程分类id’, title varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT ‘课程分类标题’, parent_id int(11) DEFAULT NULL COMMENT ‘父ID’, sort int(10) unsigned DEFAULT ‘0’ COMMENT ‘排序字段’, PRIMARY KEY (id), KEY parent_id (parent_id) ) ENGINE=MyISAM AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT=‘课程分类’;根据该前端代码给出Spring boot3结合Mybatis且数据库为Mysql5.7版本的删除操作的全部后端代码,删除的时候要求能够成功删除而且要求删除父级时全部的子级包括父级本身都要删除,删除子级时只删除该子级本身后端只有,CategoryController层,CategoryService层,CategoryMapper层和CategoryMapper.xml
时间: 2025-03-22 17:09:56 浏览: 75
<think>好的,我现在需要根据用户提供的前端代码和数据库结构,编写一个Spring Boot 3的后端删除功能。用户要求当删除父级分类时,同时删除所有子级分类和父级本身;删除子级时只删除该子级。后端需要包含Controller、Service、Mapper层以及对应的XML文件。
首先,我需要理解数据库表结构。表category有id、title、parent_id和sort字段。其中parent_id表示父分类的ID,如果为NULL则表示它是顶级分类。因此,这是一个典型的树形结构,使用邻接表模型存储。
接下来,用户的删除逻辑是:当删除一个分类时,如果是父级(即有子分类),需要删除所有子分类及其自身;如果是子级(没有子分类),则只删除自己。因此,后端需要能够递归删除所有子节点。
考虑到MySQL 5.7不支持递归查询(如WITH RECURSIVE),传统的做法是在应用层或使用存储过程来处理递归删除。这里可能需要用MyBatis在Mapper中实现递归查询,或者通过多次查询来获取所有子节点。
首先,我需要设计Mapper的方法。可能需要一个方法用来根据当前节点ID查询所有子节点ID,包括所有层级的子节点。然后,另一个方法用来批量删除这些ID。或者,使用MySQL的DELETE语句结合JOIN来删除所有子节点。
但用户指定使用MyBatis,所以可能需要先查询所有子节点ID,再执行删除。例如,在Mapper中写一个递归查询,获取当前节点及其所有子节点的ID列表,然后执行批量删除。
不过,递归查询在MySQL 5.7中可能需要使用存储过程,但这样可能不够灵活。另一种方法是在Service层递归查询子节点,收集所有需要删除的ID,然后一次性删除。这种方法可能在性能上有问题,如果层级很深的话,但实现起来比较简单。
或者,可以考虑在Mapper.xml中使用自定义的递归查询,例如通过多次自我连接,但这可能不够高效,特别是当层级不确定时。
这里可能需要一个SQL语句来获取某个节点的所有子节点。例如,使用类似如下的方法:
SELECT id FROM category WHERE parent_id = #{id} -- 第一层子节点
UNION ALL
SELECT c.id FROM category c INNER JOIN (上面的结果) t ON c.parent_id = t.id
-- 以此类推,但MySQL 5.7不支持递归CTE,所以可能需要用存储过程或者在代码中递归处理。
因此,在代码中处理可能更可行。具体来说,可以编写一个递归方法,在Service层中,先查询当前节点的所有子节点,然后递归查询每个子节点的子节点,直到没有更多子节点,收集所有ID,最后连同当前节点ID一起删除。
但是,这样可能会有多次数据库查询,可能影响性能,但考虑到分类层级通常不会太深,可能可以接受。
现在,开始编写各个层的代码:
1. Controller层:处理HTTP DELETE请求,接收分类ID,调用Service层的删除方法。
2. Service层:实现删除逻辑。首先检查该分类是否有子分类。如果有,则递归获取所有子分类ID,连同当前ID一起删除。如果没有,只删除当前ID。
但这里有个问题:如何判断该分类是否有子分类?可以通过查询parent_id等于当前ID的记录是否存在。如果有,则需要递归删除所有子节点。
或者,不管是否是父节点,直接递归删除所有子节点和自身。这样,即使删除子节点,也会检查它是否有子节点,如果有则一并删除。但根据用户的需求,删除子级时应该只删除自身,但如果该子级还有子节点,应该如何处理呢?用户的需求描述是“删除子级时只删除该子级本身”,所以如果该子级还有子节点,是否应该不允许删除?或者用户可能希望删除该子级及其所有子节点?需要明确需求。
根据用户的问题描述:“删除父级时全部的子级包括父级本身都要删除,删除子级时只删除该子级本身”。这里的“子级”可能指的是没有子节点的叶子节点。但如果有子节点的子级,可能也应该视为父级,需要递归删除。因此,可能需要明确:当删除任何一个节点时,如果它有子节点,则必须删除所有子节点;否则只删除自己。或者,可能用户希望只有当删除的是顶级父节点时才删除所有子节点,而子节点的删除仅自身。这可能不太合理,需要仔细分析。
根据用户的前端代码,当点击删除时,会弹出确认对话框,提示“确认删除该分类及其所有子分类?”,这说明无论当前分类是父级还是子级,只要它有子分类,都会删除所有子分类。或者,可能前端根据是否有子分类来显示不同的提示信息。但根据代码中的提示信息是固定的,即不管是否有子分类,都会提示删除该分类及其所有子分类。因此,后端需要确保当删除某个分类时,无论它是否有子分类,都删除它本身以及所有子分类。因此,无论该分类是父级还是子级,只要有子分类,都递归删除所有子分类。
但根据用户的问题描述,可能用户希望当删除父级时删除所有子级,而删除子级时仅删除自己。这可能需要根据当前分类是否有子分类来决定删除方式。因此,在Service层中,需要判断当前分类是否有子分类,如果有,则递归删除所有子分类和自身;如果没有,只删除自身。
这样,删除操作分为两种情况:
- 如果该分类有子节点:删除所有子节点及自身。
- 如果没有子节点:仅删除自身。
因此,Service层的逻辑应该是:
1. 根据当前分类ID,查询是否存在子节点(parent_id = 当前ID)。
2. 如果有子节点,则递归获取所有子节点的ID,连同当前ID一起删除。
3. 如果没有子节点,直接删除当前ID。
但如何高效地获取所有子节点的ID呢?这可能需要递归查询,或者使用存储过程。
在MySQL 5.7中,无法使用递归CTE,所以只能通过多次查询或者存储过程。在应用中处理的话,可以通过递归方法查询每个层级的子节点。
例如,在Service层:
private List<Integer> getAllChildIds(Integer parentId) {
List<Integer> childIds = categoryMapper.selectChildrenIdsByParentId(parentId);
List<Integer> allChildIds = new ArrayList<>(childIds);
for (Integer childId : childIds) {
allChildIds.addAll(getAllChildIds(childId));
}
return allChildIds;
}
然后,收集所有子ID,加上当前ID,执行批量删除。
但这样会产生多次数据库查询,可能效率较低,但对于层级不深的情况是可以接受的。
另一种方法是,在Mapper.xml中编写一个递归查询,通过连接查询获取所有子节点。但这在MySQL 5.7中可能需要使用存储过程。
或者,可以设计一个SQL语句,通过自连接多次查询子节点,但层级深度有限。例如,假设分类层级最多有3层,可以写多个UNION ALL,但这种方法不够灵活,不推荐。
因此,可能在Service层中使用递归方法获取所有子节点ID是可行的。
现在,具体到代码:
首先,Controller层:
@DeleteMapping("/deleteById/{id}")
public ResultVO deleteById(@PathVariable Integer id) {
categoryService.deleteCategoryAndChildren(id);
return ResultVO.success();
}
这里假设ResultVO是自定义的返回对象。
Service层:
@Service
public class CategoryService {
@Autowired
private CategoryMapper categoryMapper;
public void deleteCategoryAndChildren(Integer id) {
List<Integer> idsToDelete = new ArrayList<>();
idsToDelete.add(id);
// 递归获取所有子节点ID
getAllChildIds(id, idsToDelete);
// 执行批量删除
categoryMapper.deleteBatchIds(idsToDelete);
}
private void getAllChildIds(Integer parentId, List<Integer> ids) {
List<Integer> childIds = categoryMapper.selectChildrenIdsByParentId(parentId);
if (!childIds.isEmpty()) {
ids.addAll(childIds);
for (Integer childId : childIds) {
getAllChildIds(childId, ids);
}
}
}
}
这里,getAllChildIds方法会递归查询每个parentId的子节点,并将所有子节点ID收集到ids列表中。
然后,Mapper层:
public interface CategoryMapper {
void deleteBatchIds(@Param("ids") List<Integer> ids);
List<Integer> selectChildrenIdsByParentId(@Param("parentId") Integer parentId);
}
在Mapper.xml中:
<!-- 查询子节点ID列表 -->
<select id="selectChildrenIdsByParentId" resultType="java.lang.Integer">
SELECT id FROM category WHERE parent_id = #{parentId}
</select>
<!-- 批量删除 -->
<delete id="deleteBatchIds">
DELETE FROM category WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
这样,当调用deleteCategoryAndChildren时,会先收集所有需要删除的ID(包括自身和所有子节点),然后通过批量删除操作一次性删除。
需要注意的是,在MySQL中使用MyISAM引擎,该引擎不支持事务和外键约束。因此,删除操作没有事务管理,但如果需要保证删除的原子性,可能需要考虑使用InnoDB引擎,并添加事务注解。但用户提供的表结构使用的是MyISAM,所以这里可能不需要处理事务,或者用户可能需要调整存储引擎。
另外,关于外键的问题,由于表结构中没有外键约束,删除父节点时不会自动删除子节点,所以必须手动处理,这正是上述代码所做的。
现在,总结各层代码:
CategoryController:处理删除请求,调用Service方法。
CategoryService:递归获取所有子节点ID,执行批量删除。
CategoryMapper:定义查询子节点和批量删除的方法。
Mapper.xml:实现对应的SQL语句。
此外,需要注意MyBatis的配置是否正确,比如是否启用了批量操作,参数是否正确传递等。
可能的改进点:
- 使用事务:在Service方法上添加@Transactional,确保删除操作的原子性。即使MyISAM不支持事务,但如果有其他操作需要事务,可能需要更改存储引擎为InnoDB。
- 性能优化:对于层级很深的分类,递归查询可能导致多次数据库访问,可以考虑在数据库层使用存储过程来一次性获取所有子节点ID,但这可能增加维护成本。
- 错误处理:在Service层处理异常,比如分类不存在等情况,但前端已经做了确认提示,可能不需要特别处理。
综上,这样设计的代码应该能够满足用户的需求:删除父级时删除所有子级和自身,删除子级时仅删除自身(如果该子级没有子节点的话)。如果有子级还有子节点,则递归删除所有后代。</think>以下是基于Spring Boot 3 + MyBatis + MySQL 5.7的后端删除实现,实现递归删除所有子分类功能:
### 1. CategoryController 层
```java
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@DeleteMapping("/deleteById/{id}")
public ResultVO<String> deleteById(@PathVariable Integer id) {
categoryService.deleteCategoryWithChildren(id);
return ResultVO.success("删除成功");
}
}
```
### 2. CategoryService 层
```java
@Service
public class CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Transactional
public void deleteCategoryWithChildren(Integer id) {
// 获取所有需要删除的ID(包含自身及所有子节点)
List<Integer> deleteIds = new ArrayList<>();
deleteIds.add(id); // 添加当前节点
findChildIds(id, deleteIds); // 递归获取子节点
// 执行批量删除
if (!deleteIds.isEmpty()) {
categoryMapper.deleteBatchIds(deleteIds);
}
}
// 递归查找所有子节点ID
private void findChildIds(Integer parentId, List<Integer> deleteIds) {
List<Integer> childIds = categoryMapper.selectChildrenIds(parentId);
for (Integer childId : childIds) {
deleteIds.add(childId);
findChildIds(childId, deleteIds); // 递归查询子节点
}
}
}
```
### 3. CategoryMapper 接口层
```java
@Mapper
public interface CategoryMapper {
// 根据父ID查询子节点ID列表
List<Integer> selectChildrenIds(@Param("parentId") Integer parentId);
// 批量删除操作
int deleteBatchIds(@Param("ids") List<Integer> ids);
}
```
### 4. CategoryMapper.xml
```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.CategoryMapper">
<!-- 查询子节点ID列表 -->
<select id="selectChildrenIds" resultType="java.lang.Integer">
SELECT id
FROM category
WHERE parent_id = #{parentId}
</select>
<!-- 批量删除 -->
<delete id="deleteBatchIds">
DELETE FROM category
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>
```
### 实现逻辑说明
1. **递归查询子节点**:通过`findChildIds`方法递归查询所有子节点ID,时间复杂度为$O(n)$,适用于常规树形结构深度
2. **批量删除**:使用`DELETE ... IN`语句实现高效批量删除,时间复杂度$O(1)$
3. **事务控制**:`@Transactional`注解保证删除操作的原子性
4. **树形结构处理**:当删除父节点时,会连带删除其所有子节点;删除叶子节点时仅删除自身
### 性能优化建议
1. **索引优化**:确保`parent_id`字段已建立索引(已有`KEY parent_id (parent_id)`)
2. **批量操作**:使用`<foreach>`标签实现批量SQL,减少数据库交互次数
3. **异步删除**:如需处理超大层级,可通过@Async实现异步删除
### 数据库兼容性说明
由于使用标准的SQL语法:
- 完全兼容MySQL 5.7版本
- 支持MyISAM存储引擎(但建议改用InnoDB以保证事务完整性)
- 无递归CTE语法依赖
> 注意:ResultVO需要自定义实现,包含`success()`和错误处理等方法。
阅读全文
相关推荐
















