249 lines
5.8 KiB
Markdown
249 lines
5.8 KiB
Markdown
# Banner批量排序SQL语法Bug修复报告
|
||
|
||
## 🐛 问题描述
|
||
|
||
### 错误信息
|
||
```
|
||
SQLSyntaxErrorException: You have an error in your SQL syntax;
|
||
check the manual that corresponds to your MySQL server version for the right syntax to use near
|
||
'UPDATE banner SET sort_order = 2, update_time =' at line 6
|
||
```
|
||
|
||
### 错误位置
|
||
- **方法**: `BannerMapper.batchUpdateSortOrder`
|
||
- **文件**: `BannerMapper.xml`
|
||
- **操作**: 批量更新Banner排序
|
||
|
||
---
|
||
|
||
## 🔍 根本原因分析
|
||
|
||
### 问题SQL语法
|
||
```xml
|
||
<!-- ❌ 错误的SQL语法 -->
|
||
<update id="batchUpdateSortOrder">
|
||
<foreach collection="banners" item="banner" separator=";">
|
||
UPDATE banner SET
|
||
sort_order = #{banner.sortOrder},
|
||
update_time = NOW()
|
||
WHERE id = #{banner.id}
|
||
</foreach>
|
||
</update>
|
||
```
|
||
|
||
### 生成的错误SQL
|
||
```sql
|
||
UPDATE banner SET sort_order = ?, update_time = NOW() WHERE id = ? ;
|
||
UPDATE banner SET sort_order = ?, update_time = NOW() WHERE id = ? ;
|
||
UPDATE banner SET sort_order = ?, update_time = NOW() WHERE id = ? ;
|
||
...
|
||
```
|
||
|
||
### 问题原因
|
||
1. **多语句分隔**: 使用分号分隔多个UPDATE语句
|
||
2. **MySQL限制**: MySQL的PreparedStatement不支持多语句执行
|
||
3. **MyBatis误用**: `separator=";"` 不适用于UPDATE操作
|
||
|
||
---
|
||
|
||
## ✅ 修复方案
|
||
|
||
### 新的SQL实现
|
||
```xml
|
||
<!-- ✅ 正确的SQL语法 -->
|
||
<update id="batchUpdateSortOrder">
|
||
UPDATE banner
|
||
SET sort_order = CASE
|
||
<foreach collection="banners" item="banner">
|
||
WHEN id = #{banner.id} THEN #{banner.sortOrder}
|
||
</foreach>
|
||
ELSE sort_order
|
||
END,
|
||
update_time = NOW()
|
||
WHERE id IN
|
||
<foreach collection="banners" item="banner" open="(" separator="," close=")">
|
||
#{banner.id}
|
||
</foreach>
|
||
</update>
|
||
```
|
||
|
||
### 生成的正确SQL
|
||
```sql
|
||
UPDATE banner
|
||
SET sort_order = CASE
|
||
WHEN id = 1 THEN 5
|
||
WHEN id = 2 THEN 2
|
||
WHEN id = 3 THEN 3
|
||
WHEN id = 4 THEN 4
|
||
WHEN id = 5 THEN 1
|
||
ELSE sort_order
|
||
END,
|
||
update_time = NOW()
|
||
WHERE id IN (1, 2, 3, 4, 5)
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 修复前后对比
|
||
|
||
### 修复前
|
||
| 问题 | 影响 |
|
||
|------|------|
|
||
| ❌ 多语句UPDATE分隔 | 语法错误,无法执行 |
|
||
| ❌ 使用分号分隔符 | MySQL PreparedStatement不支持 |
|
||
| ❌ 执行失败 | 批量排序功能无法使用 |
|
||
|
||
### 修复后
|
||
| 改进 | 效果 |
|
||
|------|------|
|
||
| ✅ 单一CASE WHEN UPDATE | 标准SQL语法,完全兼容 |
|
||
| ✅ 批量条件更新 | 一次执行更新多个记录 |
|
||
| ✅ 高效执行 | 比多次单独UPDATE更快 |
|
||
|
||
---
|
||
|
||
## 🧪 测试验证
|
||
|
||
### 测试数据示例
|
||
```javascript
|
||
// 批量排序数据
|
||
[
|
||
{"id": 1, "sortOrder": 5},
|
||
{"id": 2, "sortOrder": 2},
|
||
{"id": 3, "sortOrder": 3},
|
||
{"id": 4, "sortOrder": 4},
|
||
{"id": 5, "sortOrder": 1}
|
||
]
|
||
```
|
||
|
||
### 生成的SQL验证
|
||
```sql
|
||
UPDATE banner
|
||
SET sort_order = CASE
|
||
WHEN id = 1 THEN 5
|
||
WHEN id = 2 THEN 2
|
||
WHEN id = 3 THEN 3
|
||
WHEN id = 4 THEN 4
|
||
WHEN id = 5 THEN 1
|
||
ELSE sort_order
|
||
END,
|
||
update_time = NOW()
|
||
WHERE id IN (1, 2, 3, 4, 5)
|
||
```
|
||
|
||
### 预期结果
|
||
- ✅ SQL语法正确,可以正常执行
|
||
- ✅ 所有指定ID的记录都会更新排序值
|
||
- ✅ 未指定的记录保持原有排序值不变
|
||
- ✅ 所有记录的 `update_time` 都会更新
|
||
|
||
---
|
||
|
||
## 🛡️ SQL最佳实践
|
||
|
||
### 1. 批量更新策略
|
||
```sql
|
||
-- ✅ 推荐:使用CASE WHEN进行批量更新
|
||
UPDATE table_name
|
||
SET column1 = CASE
|
||
WHEN condition1 THEN value1
|
||
WHEN condition2 THEN value2
|
||
ELSE column1
|
||
END
|
||
WHERE id IN (id_list);
|
||
|
||
-- ❌ 避免:多语句分隔
|
||
UPDATE table SET col=val WHERE id=1;
|
||
UPDATE table SET col=val WHERE id=2;
|
||
```
|
||
|
||
### 2. MyBatis批量操作
|
||
```xml
|
||
<!-- ✅ 正确的批量更新 -->
|
||
<update id="batchUpdate">
|
||
UPDATE table SET field = CASE
|
||
<foreach collection="list" item="item">
|
||
WHEN id = #{item.id} THEN #{item.value}
|
||
</foreach>
|
||
ELSE field
|
||
END
|
||
WHERE id IN
|
||
<foreach collection="list" item="item" open="(" separator="," close=")">
|
||
#{item.id}
|
||
</foreach>
|
||
</update>
|
||
|
||
<!-- ❌ 错误的多语句方式 -->
|
||
<update id="batchUpdateWrong">
|
||
<foreach collection="list" item="item" separator=";">
|
||
UPDATE table SET field = #{item.value} WHERE id = #{item.id}
|
||
</foreach>
|
||
</update>
|
||
```
|
||
|
||
### 3. 性能优势
|
||
| 方式 | SQL语句数 | 网络往返 | 事务处理 |
|
||
|------|-----------|----------|----------|
|
||
| **CASE WHEN批量** | 1条 | 1次 | 原子操作 |
|
||
| **多次单独UPDATE** | N条 | N次 | 需要显式事务 |
|
||
|
||
---
|
||
|
||
## 📋 文件变更清单
|
||
|
||
### 修改文件
|
||
- ✅ `src/main/resources/mapper/BannerMapper.xml` - 修复 `batchUpdateSortOrder` SQL语法
|
||
|
||
### 新增文件
|
||
- ✅ `docs/banner-batch-sort-bug-fix.md` - 本修复报告
|
||
|
||
---
|
||
|
||
## 🎯 修复效果
|
||
|
||
### ✅ 解决的问题
|
||
1. **SQL语法错误**: 消除了多语句分隔导致的语法错误
|
||
2. **执行效率**: 从多次UPDATE改为单次批量UPDATE
|
||
3. **事务安全**: 确保批量操作的原子性
|
||
4. **代码质量**: 使用标准的SQL批量更新模式
|
||
|
||
### 🚨 注意事项
|
||
1. **测试验证**: 确保批量排序功能正常工作
|
||
2. **性能监控**: 观察批量更新的执行时间
|
||
3. **数据一致性**: 验证所有记录都正确更新
|
||
|
||
---
|
||
|
||
## 🧪 测试建议
|
||
|
||
### 使用测试页面验证
|
||
1. 访问 `/test_banner_admin.html`
|
||
2. 点击"获取Banner列表"加载数据
|
||
3. 点击"测试批量排序"生成随机排序
|
||
4. 点击"批量更新排序"执行更新
|
||
5. 重新加载列表验证排序是否正确
|
||
|
||
### 命令行测试
|
||
```bash
|
||
# 测试批量排序接口
|
||
curl -X PUT "http://localhost:8081/admin/banners/batch-sort" \
|
||
-H "Authorization: Bearer {admin_token}" \
|
||
-H "Content-Type: application/json" \
|
||
-d '[{"id":1,"sortOrder":5},{"id":2,"sortOrder":2}]'
|
||
```
|
||
|
||
---
|
||
|
||
**修复状态**: ✅ 已完成
|
||
**测试状态**: ⏳ 待验证
|
||
**风险等级**: 低(只影响批量排序功能)
|
||
**部署要求**: 重启服务器使MyBatis配置生效
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|