spring 事务中先删除再插入后唯一键冲突 delete then insert duplicate key

发布于:2023-09-15 ⋅ 阅读:(83) ⋅ 点赞:(0)

今天在同事碰到了一个比较有意思的问题,为了实现某个场景中的数据更新和删除,想通过 delete all entities 然后 insert new entities 的方式减少判断数据是否删除的操作,结果由于表内有其他唯一索引报错唯一键冲突。
后面在 debug 的过程中,用 evaluate 查询数据确实不在,但是 insert 的时候会报唯一键冲突导致插入失败事务回滚。

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [unique_format_content_language] ...
  • 下面大概复现下问题
    mysql 版本 5.x.x 事务隔离级别 read commit
public class User {

    private Long id; // primary key

    private Long uk; // foreign key

    private String className; // key

    private String name;

    private int age;
    
}

大概通过下面的方式进行先删除,再插入。这里通过班级名称来统一删除相关用户,再插入传进来的最新版本。

@Transactional
   public boolean update(List<User> users, String className) {
       userRepository.deleteByClassName(className);
       userRepository.insertBatch(users);
       return true;
   }

这里由于通过 className 这个普通索引进行删除,在事务中仅对这些数据做了标记,并没有真正从磁盘中删除。并且也没有对唯一索引进行更新,从而导致后面插入新版本数据的过程中,如果是修改的数据那么会在唯一索引找到重复的键从而导致冲突。
这里的解决方法是可以通过 pk 或者 uk 进行删除,这样会去更新索引,从而避免冲突。

todo:事务中索引的变更

当然这里也可以将班级的其他人查出来,然后通过比较找到 insertList、updateList 以及 deleteList。


网站公告

今日签到

点亮在社区的每一天
去签到