主键冲突问题

1. 背景

更新商品信息,除了更新商品信息表中的基本数据,也需要更新对应的标签表中的信息。

这里采用了先删除原有的标签表中的对应信息,重新插入标签信息。

在更新商品信息的过程中,除了需要更新商品信息表中的基本数据外,还必须确保对应的标签信息表也得到适当的更新。这是因为商品和标签之间存在紧密的关联关系,一个商品通常会有多个标签,标签能够有效地描述商品的特性和分类。

为了实现这一点,我们采取了以下策略:

  1. 删除原有标签信息 :在更新商品信息时,首先需要清理与该商品相关的所有旧标签。这一步骤是通过调用标签数据访问层(productTagMapper)的删除方法实现的,具体来说,我们通过deleteByProductIds方法删除与当前商品ID相匹配的所有标签。这是为了避免标签冗余和过时信息对商品的描述产生干扰。

  2. 重新插入标签信息 :在删除了旧标签后,我们会获取新的标签信息。这些标签信息存储在接收的ProductDTO对象中。通过对标签列表的遍历,我们将每个标签的productId属性设置为当前商品的ID,从而确保标签与商品的关联性。随后,我们使用productTagService.saveBatch方法批量插入新的标签信息。这一步骤确保了商品的标签能够实时反映出最新的状态和特性。

java 复制代码
@Transactional
@Override
public void updateWithTags(ProductDTO productDTO) {
    // 1. 更新商品信息
    Product product = new Product();
    BeanUtils.copyProperties(productDTO, product);
    super.updateById(product);

    // 2. 更新标签信息
    // 2.1 删除原有标签
    List<Long> productIds = Collections.singletonList(product.getId());
    productTagMapper.deleteByProductIds(productIds);

    // 2.2 插入新标签
    List<ProductTag> tags = productDTO.getTags();
    if (tags != null && !tags.isEmpty()) {
        for (ProductTag tag : tags) {
            tag.setProductId(product.getId());
        }
        productTagService.saveBatch(tags);
    }
}

2. 问题分析

  • 删除操作未提交 : 在事务性的操作中,deleteByProductIds 删除的记录不会立即被数据库确认删除,而是要等整个事务提交后才生效。在同一事务内执行 delete 后再执行 insert,如果插入的数据带有相同的主键 ID,数据库会认为这是一个重复插入操作,因为此时删除操作尚未真正生效。也就是说,数据库在事务未提交前,仍然视这些被删除的记录为存在

  • MyBatis-Plus 主键冲突检查 : 当 ID 被指定时,MyBatis-Plus 会认为这是一次插入已有主键的数据,而不会生成新主键。即便数据库中的原记录已"标记删除",在事务未提交前,数据库仍然会检测到重复的主键,因而报错。

  • 主键冲突的检查机制 : 在大多数关系型数据库中,即便记录已删除,主键冲突的检查机制仍然会对同一个事务内的 insert 操作报错。这是为了防止未提交事务之间的重复主键插入,确保数据完整性。

由于事务未提交,删除操作对后续插入操作是不可见的,因此会触发主键冲突。

3. 解决方案

重新插入时,先清空ID,强制 MyBatis-Plus 生成新的主键值。这可以确保每次插入时,都会生成新的主键,而不会因为事务内的"逻辑删除"导致主键冲突问题。

java 复制代码
// 2.2 插入新标签
    List<ProductTag> tags = productDTO.getTags();
    if (tags != null && !tags.isEmpty()) {
        for (ProductTag tag : tags) {
            tag.setId(null);  // 清空 ID,避免冲突
            tag.setProductId(product.getId());
        }
        productTagService.saveBatch(tags);
    }

4. 总结

在事务性的操作中,删除的记录不会立即被数据库确认删除,而是要等整个事务提交后才生效。所以如果要删除后重新插入数据,在插入之前要清空id,强制 MyBatis-Plus 生成新的主键值,避免主键冲突。

相关推荐
Paraverse_徐志斌3 小时前
MySQL 线上大表 DDL 如何避免锁表(pt-online-schema-change)
数据库·mysql·ddl·mysql锁·锁表·pt-osc
哈哈幸运4 小时前
MySQL运维三部曲初级篇:从零开始打造稳定高效的数据库环境
linux·运维·数据库·mysql·性能优化
愚公搬代码4 小时前
【愚公系列】《Python网络爬虫从入门到精通》055-Scrapy_Redis分布式爬虫(安装Redis数据库)
数据库·爬虫·python
pwzs4 小时前
深入浅出 MVCC:MySQL 并发背后的多版本世界
数据库·后端·mysql
大熊猫今天吃什么4 小时前
【一天一坑】空数组,使用 allMatch 默认返回true
前端·数据库
双叶8365 小时前
(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)
c语言·数据库·单片机·嵌入式硬件·mongodb·51单片机·nosql
XY.散人5 小时前
初识Redis · C++客户端list和hash
数据库·redis·缓存
码上飞扬5 小时前
深入 MySQL 高级查询:JOIN、子查询与窗口函数的实用指南
数据库·mysql
海洋与大气科学6 小时前
【matlab】地图上的小图
开发语言·数据库·matlab
Geek__19926 小时前
Sqlite3交叉编译全过程
jvm·数据库·sqlite