问题背景
- 开发过程中,为了保证数据的唯一性,对某个旧表建立一个联合索引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) 新增了资源的翻译

- 不过结合hibernate的sql日志可以发现:insert语句先执行了。在insert语句执行前,并没有执行delete方法

- 所以可以猜测应该是jpa框架的一些特性导致的
- 通过google关键字检索,找到github上对应问题的一个issue
- 该issue中很多人也遇到了delete then save时,唯一索引报错问题
- 通过查看该issue的讨论记录,可找到对应的两个解决方案(经过验证,这两个方案都可行)
- 使用deleteInBatch代替delete
- delete之后,手动调用flush方法

总结
遗留问题
- spring-data-jpa的delete then save操作,为啥delete不执行?jpa为啥要如此设计?