添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 开发过程中,为了保证数据的唯一性,对某个旧表建立一个联合索引unique_format_content_language(format_content_id,language_code)。加了唯一索引之后,有个MQ联动的业务场景,消费一直报错:
  • org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [unique_format_content_language] ...
    
  • 该业务场景MQ消费的基本逻辑(以下步骤是在一个事务中):
  • 接收数据变更的MQ
  • 增量翻译数据
  • 删除旧翻译
  • 保存新翻译
  • dao使用的技术为spring-data-jpa,使用的方式如下:
  • @Repository
    
    
    
    
        
    
    public interface KnowledgeContentTranslationRepository extends JpaRepository<ContentTranslationItem, String>{
    

    问题定位过程及解决方案

  • 尝试用debug模式,在出错的地方打了个断点,从数据来看是没什么问题的。
  • knowledgeContentTranslationRepository.delete(deleteContentTranslationItems) 删除了资源的翻译
  • knowledgeContentTranslationRepository.save(addContentTranslationItems) 新增了资源的翻译
  • spring-data-jpa在一个事务中,先调用delete方法,再调用save方法时,事务提交时,并不会先执行delete的语句,而是直接执行insert语句
  • 在这种情况下,如果表有唯一索引,就有可能出现唯一索引冲突。异常信息为:
  • org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [unique_format_content_language] ...
    
  • 对于delete then save,唯一索引报错的场景,可以考虑通过一下两种方案来避免报错
  • 使用deleteInBatch代替delete
  • 推荐。但deleteInBatch底层是将id通过or的形式拼接成sql,通过形如:delete from [table_name] where id=? or id=?...的sql进行执行的。当删除的数据过大时,可能会出现java.lang.StackOverflowError异常
  • delete之后,手动调用flush方法
  • 不怎么推荐。因为flush方法会该方法之前的所有数据都同步到DB,可能会有性能问题
  • Spring Boot
    私信