Files
cattleTransportation/VEHICLE_DELETE_FINAL_FIX.md

332 lines
9.3 KiB
Markdown
Raw Normal View History

2025-10-29 17:33:32 +08:00
# 车辆删除功能最终修复 - 使用 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<Vehicle> 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()` 不适合修改逻辑删除标记。