You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot+MySQL:不依赖数据库唯一约束实现Topic唯一校验

解决方案

核心思路

确保仅活跃状态的Topic的text字段唯一,允许已软删除的同名Topic存在,同时支持重新创建同名活跃Topic,且删除新创建的Topic时无冲突。


具体实现步骤

1. 业务层前置校验(核心)

在创建或更新Topic时,先执行查询校验:

  • 创建Topic:检查是否存在status = 'active'且text与待创建值相同的记录。若存在则拒绝创建;若不存在(包括仅存在已删除的同名记录)则允许创建。
    示例SQL:
    SELECT COUNT(id) FROM topic WHERE text = ? AND status = 'active'
    
  • 更新Topic的text字段:若原Topic处于活跃状态,需检查新text是否被其他活跃Topic占用;若原Topic已删除,更新text时无需校验(通常删除状态的Topic不应被修改)。

2. 并发场景下的事务与锁控制

高并发场景中,可能出现多个请求同时校验通过后插入的情况,需通过事务+锁避免重复创建:

  • 悲观锁方案:在校验查询时添加FOR UPDATE(适用于InnoDB引擎),锁定符合条件的记录(无记录则锁定间隙),防止其他请求同时插入:
    -- 开启事务后执行
    SELECT id FROM topic WHERE text = ? AND status = 'active' FOR UPDATE
    
    若查询返回结果,回滚事务并返回错误;否则插入新的活跃Topic后提交事务。
  • 乐观锁方案:给topic表添加version字段,创建/更新时校验版本号,适用于并发量中等、冲突概率较低的场景。

3. 软删除逻辑优化

删除Topic时,仅将status字段更新为deleted即可,无需处理其他已存在的同名删除记录。因为没有全局或复合唯一约束,更新操作不会触发冲突。

4. 可选:数据库层面添加部分唯一索引(辅助校验)

若数据库支持(如MySQL 5.7+、PostgreSQL),可创建部分唯一索引,仅对活跃状态的记录施加唯一约束,作为业务层校验的兜底:

-- MySQL 示例
CREATE UNIQUE INDEX idx_topic_text_active ON topic(text) WHERE status = 'active';

-- PostgreSQL 示例
CREATE UNIQUE INDEX idx_topic_text_active ON topic(text) WHERE status = 'active';

该索引仅阻止活跃状态的同名Topic,不限制已删除的同名记录,完美匹配需求。它不会像复合唯一约束那样导致删除失败,因为删除时只是修改status,不会触发索引冲突。


为什么之前的方案不可行?

  • 单一text唯一约束:无法重新创建已软删除的同名Topic;
  • text + status复合唯一约束:重新创建同名活跃Topic后,删除该Topic时会将status改为deleted,与之前已删除的同名记录触发复合唯一冲突,导致删除失败。

内容的提问来源于stack exchange,提问作者Sanket Kale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:12:39