# 车辆删除功能最终修复 - 使用 MyBatis-Plus 的 @TableLogic ## 问题描述 用户反馈:**删除车辆返回成功,但前端页面依然显示该记录**。 后端日志显示: ``` UPDATE vehicle SET license_plate=?, ..., update_time=?, updated_by=? WHERE id=? AND is_delete=0 Updates: 1 ``` **关键发现**:UPDATE 语句中**没有 `is_delete` 字段**! ## 问题根源 ### @TableLogic 注解的副作用 `Vehicle` 实体类使用了 `@TableLogic` 注解: ```java @TableLogic(value = "0", delval = "1") @TableField("is_delete") private Integer isDelete; ``` **@TableLogic 的行为**: 1. ✅ **SELECT**: 自动添加 `WHERE is_delete = 0` 2. ✅ **DELETE**: 自动转换为 `UPDATE SET is_delete = 1` 3. ❌ **UPDATE**: `is_delete` 字段**被排除在 SET 子句之外**! ### 错误的实现方式 ```java // ❌ 错误:使用 updateById 无法修改 is_delete 字段 Vehicle vehicle = vehicleMapper.selectById(id); vehicle.setIsDelete(1); // 这个设置会被忽略! vehicleMapper.updateById(vehicle); // 生成的 SQL(注意没有 is_delete): UPDATE vehicle SET license_plate=?, car_front_photo=?, ..., update_time=?, updated_by=? WHERE id=? AND is_delete=0 ``` **结果**: - `is_delete` 字段没有被更新,仍然是 `0` - 记录依然存在,前端列表继续显示 - 虽然返回 `Updates: 1`(更新了其他字段),但逻辑删除失败 ## 最终解决方案 ### 使用 MyBatis-Plus 的 deleteById() 方法 **正确做法**:利用 `@TableLogic` 注解,直接调用 `deleteById()` ```java /** * 删除车辆(逻辑删除,使用 MyBatis-Plus 的 deleteById 自动处理) * 因为实体类使用了 @TableLogic 注解,deleteById 会自动将 is_delete 设置为 1 */ @Override @Transactional public AjaxResult deleteVehicle(Integer id) { System.out.println("[VEHICLE-DELETE] 开始逻辑删除车辆,ID: " + id); if (id == null) { System.out.println("[VEHICLE-DELETE] 删除失败:车辆ID为空"); return AjaxResult.error("车辆ID不能为空"); } // 查询车辆是否存在(@TableLogic 会自动过滤 is_delete=1 的记录) Vehicle vehicle = vehicleMapper.selectById(id); if (vehicle == null) { System.out.println("[VEHICLE-DELETE] 删除失败:车辆不存在或已被删除,ID: " + id); return AjaxResult.error("车辆不存在"); } System.out.println("[VEHICLE-DELETE] 车辆信息 - 车牌号: " + vehicle.getLicensePlate() + ", 当前 is_delete: " + vehicle.getIsDelete()); // 获取当前用户ID(用于日志记录) Integer userId = SecurityUtil.getCurrentUserId(); // ✅ 使用 MyBatis-Plus 的 deleteById 方法 // 因为实体类有 @TableLogic 注解,这会自动执行逻辑删除(UPDATE SET is_delete=1) // 而不是物理删除(DELETE FROM) boolean success = vehicleMapper.deleteById(id) > 0; if (success) { System.out.println("[VEHICLE-DELETE] ✅ 逻辑删除成功,车牌号: " + vehicle.getLicensePlate() + ", 操作人ID: " + userId); return AjaxResult.success("删除车辆成功"); } else { System.out.println("[VEHICLE-DELETE] ❌ 逻辑删除失败,车牌号: " + vehicle.getLicensePlate()); return AjaxResult.error("删除车辆失败"); } } ``` ### 生成的 SQL **使用 deleteById() 后**: ```sql UPDATE vehicle SET is_delete = 1 WHERE id = 3; ``` **完美!** 只更新 `is_delete` 字段,不涉及其他字段。 ## MyBatis-Plus @TableLogic 工作原理 ### 1. SELECT 操作 ```java Vehicle vehicle = vehicleMapper.selectById(1); ``` **生成的 SQL**: ```sql SELECT * FROM vehicle WHERE id = 1 AND is_delete = 0 ``` ### 2. DELETE 操作(逻辑删除) ```java vehicleMapper.deleteById(1); ``` **生成的 SQL**: ```sql UPDATE vehicle SET is_delete = 1 WHERE id = 1 ``` ### 3. UPDATE 操作(注意!) ```java vehicle.setIsDelete(1); vehicleMapper.updateById(vehicle); ``` **生成的 SQL**: ```sql -- ❌ is_delete 不在 SET 子句中! UPDATE vehicle SET license_plate=?, car_front_photo=?, ..., update_time=? WHERE id=? AND is_delete=0 ``` **结论**: - ✅ **删除时用 `deleteById()`**,它会自动设置 `is_delete = 1` - ❌ **不要用 `updateById()`** 来修改 `is_delete` 字段 ## 修复前后对比 | 对比项 | 修复前(错误) | 修复后(正确) | |--------|----------------|----------------| | 方法调用 | `updateById(vehicle)` | `deleteById(id)` | | 生成的 SQL | `UPDATE SET 所有字段...` | `UPDATE SET is_delete=1` | | is_delete 是否更新 | ❌ 否(被排除) | ✅ 是 | | 其他字段是否更新 | ✅ 是(不必要) | ❌ 否(只更新必要字段) | | 性能 | 差(更新所有字段) | 好(只更新1个字段) | | 逻辑删除是否生效 | ❌ 否 | ✅ 是 | ## 为什么之前的方案失败 ### 尝试1:使用 updateById ```java vehicle.setIsDelete(1); vehicleMapper.updateById(vehicle); ``` **失败原因**:`@TableLogic` 会排除 `is_delete` 字段 ### 尝试2:使用 LambdaUpdateWrapper ```java LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.set(Vehicle::getIsDelete, 1); vehicleMapper.update(null, wrapper); ``` **问题**:代码复杂,而且可能与 `@TableLogic` 冲突 ### 最终方案:直接使用 deleteById ```java vehicleMapper.deleteById(id); ``` **优势**: - ✅ 代码简洁 - ✅ 利用 MyBatis-Plus 的特性 - ✅ 自动处理逻辑删除 - ✅ 与 `@TableLogic` 完美配合 ## 测试步骤 ### 1. 重启后端服务 确保新的代码生效。 ### 2. 清除浏览器缓存 强制刷新前端页面(Ctrl+F5)。 ### 3. 测试删除功能 1. 进入"车辆管理"页面 2. 选择要删除的车辆,例如:`鄂A 66662` 3. 点击"删除"按钮 4. 确认删除 ### 4. 观察后端日志 **修复后应该看到**: ``` [VEHICLE-DELETE] 开始逻辑删除车辆,ID: 3 [VEHICLE-DELETE] 车辆信息 - 车牌号: 鄂A 66662, 当前 is_delete: 0 UPDATE vehicle SET is_delete=1 WHERE id=3 [VEHICLE-DELETE] ✅ 逻辑删除成功,车牌号: 鄂A 66662, 操作人ID: 11 ``` **关键点**: - ✅ SQL 中只有 `SET is_delete=1` - ✅ 没有更新其他字段 - ✅ 日志显示"逻辑删除成功" ### 5. 验证前端列表 - ✅ 列表自动刷新 - ✅ 已删除的车辆不再显示 - ✅ 显示"删除成功"提示 ### 6. 验证数据库 ```sql -- 查看该记录 SELECT id, license_plate, is_delete, update_time FROM vehicle WHERE id = 3; -- 预期结果: -- id | license_plate | is_delete | update_time -- 3 | 鄂A 66662 | 1 | 2025-10-29 17:00:00 ``` ```sql -- 前端列表查询(不应包含该记录) SELECT * FROM vehicle WHERE is_delete = 0; ``` ## 常见问题排查 ### Q1: 删除后前端列表还显示该记录? **检查**: 1. 后端服务是否重启 2. 浏览器缓存是否清除(Ctrl+F5) 3. 后端日志中 SQL 是否为 `UPDATE SET is_delete=1` 4. 数据库中该记录的 `is_delete` 是否为 `1` ### Q2: 后端日志显示 SQL 中没有 is_delete 字段? **原因**: 代码没有更新,还在使用 `updateById()` **解决**: 1. 确认代码已修改为 `deleteById()` 2. 重新编译:`mvn clean compile` 3. 重启后端服务 ### Q3: 删除后报错 "车辆不存在"? **原因**: 记录已经被删除(`is_delete = 1`),`selectById` 自动过滤了它 **解决**: 正常现象,说明删除成功。前端应该已经刷新列表。 ### Q4: 如何查看所有已删除的记录? **SQL**: ```sql SELECT * FROM vehicle WHERE is_delete = 1; ``` ### Q5: 如何恢复已删除的记录? **SQL**: ```sql UPDATE vehicle SET is_delete = 0, update_time = NOW() WHERE id = 3; ``` ## MyBatis-Plus @TableLogic 最佳实践 ### ✅ 推荐做法 1. **删除操作**:使用 `deleteById()` 或 `deleteBatchIds()` ```java vehicleMapper.deleteById(id); // 逻辑删除 ``` 2. **查询操作**:正常使用,自动过滤已删除记录 ```java vehicleMapper.selectById(id); // 自动添加 is_delete=0 ``` 3. **更新操作**:不要尝试修改 `is_delete` 字段 ```java vehicle.setLicensePlate("新车牌"); vehicleMapper.updateById(vehicle); // is_delete 不会被更新 ``` ### ❌ 避免的做法 1. **不要用 updateById 修改 is_delete** ```java // ❌ 错误:不会生效 vehicle.setIsDelete(1); vehicleMapper.updateById(vehicle); ``` 2. **不要在 WHERE 条件中手动添加 is_delete** ```java // ❌ 多余:@TableLogic 会自动添加 queryWrapper.eq(Vehicle::getIsDelete, 0); ``` 3. **不要尝试物理删除** ```java // ❌ 这仍然是逻辑删除,不是物理删除 vehicleMapper.deleteById(id); ``` ## 总结 ✅ **最终修复方案**: - 使用 `vehicleMapper.deleteById(id)` 执行逻辑删除 - 让 MyBatis-Plus 的 `@TableLogic` 自动处理 `is_delete` 字段 - 代码简洁,性能优秀,符合框架设计理念 ✅ **核心原理**: - `@TableLogic` 会在 DELETE 操作时自动转换为 UPDATE - 不要用 UPDATE 操作来修改逻辑删除标记 - 利用框架特性,而不是绕过它 ✅ **用户体验**: - 删除成功后列表自动刷新 - 已删除的记录立即消失 - 操作简单,性能优秀 🎯 **关键教训**: 当框架提供了特定功能(如 `@TableLogic`)时,应该按照框架的设计意图使用,而不是尝试绕过或覆盖它。`deleteById()` 是正确的删除方式,`updateById()` 不适合修改逻辑删除标记。