开发过程中,为了保证数据的唯一性,对某个旧表建立一个联合索引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
- 695
-
基督山伯爵_Neo
Spring
Spring Boot
- 394
-
抢老婆酸奶的小肥仔
Spring Boot
- 434
-
Strive_MY
Spring Cloud
Spring Boot
- 334
-
Fururur
MySQL
Spring Boot
- 1820
-
zooooooooy
Spring Boot
JavaScript
- 3154
-
Java架构历程
Spring Boot
- 3639
-
Robod
Tomcat
Spring Boot
- 6.2w
-
Cosolar
Spring
Spring Boot
- 666
-
飘渺Jam
领域驱动设计
Spring Boot