JPARepository.save未检测到SQL Server主键重复异常问题
问题原因分析
你遇到的情况是JPA中JpaRepository.save()方法的默认行为导致的:
- 调用
save()时,JPA会先判断实体的主键是否已存在于数据库中 - 如果主键已存在,不会执行插入操作,而是执行更新操作,将现有记录的非主键字段替换为新实体的字段值
- 第二次调用
save()时,虽然你创建了新的实体对象,但复合主键(BEGIN_VERSION, END_VERSION)和数据库中已有的记录一致,JPA自动执行了更新而非插入,因此不会触发SQL Server的主键冲突异常
解决方案
如果你希望重复插入同主键记录时触发主键冲突异常(即只允许插入新记录,不允许更新),可以采用以下两种方式:
方式一:先查询再判断
在执行save前,先通过主键查询数据库是否存在对应记录,存在则直接返回失败或抛出异常:
@Transactional(transactionManager = "standardizedDataSourceTransactionManager", propagation = Propagation.REQUIRES_NEW) public boolean processSave(long beginVersion, long endVersion, String hostId) { boolean success = false; try { TableFlattenProcessingInfoCPK cpk = new TableFlattenProcessingInfoCPK(beginVersion, endVersion); // 先查询是否存在该主键的记录 if (flattenProcessingInfoRepo.existsById(cpk)) { log.info("Batch already taken up for processing"); return false; } TableFlattenProcessingInfo tableFlattenProcessingInfo = new TableFlattenProcessingInfo(); tableFlattenProcessingInfo.setTableFlattenProcessingInfoCPK(cpk); tableFlattenProcessingInfo.setHostId(hostId); tableFlattenProcessingInfo.setProcessingDateTime(DateUtil.convertUTCToEST(new Date(), TimeZone.getTimeZone("UTC"), TimeZone.getTimeZone("America/New_York"))); flattenProcessingInfoRepo.save(tableFlattenProcessingInfo); success = true; } catch (Exception e) { log.info("Error Inserting Flatten Batch -> {}", e.getMessage()); success = false; } return success; }
方式二:使用EntityManager.persist()强制插入
persist()方法只负责插入新实体,如果主键已存在会直接抛出EntityExistsException,不会执行更新:
@Autowired private EntityManager entityManager; @Transactional(transactionManager = "standardizedDataSourceTransactionManager", propagation = Propagation.REQUIRES_NEW) public boolean processSave(long beginVersion, long endVersion, String hostId) { boolean success = false; try { TableFlattenProcessingInfoCPK cpk = new TableFlattenProcessingInfoCPK(beginVersion, endVersion); TableFlattenProcessingInfo tableFlattenProcessingInfo = new TableFlattenProcessingInfo(); tableFlattenProcessingInfo.setTableFlattenProcessingInfoCPK(cpk); tableFlattenProcessingInfo.setHostId(hostId); tableFlattenProcessingInfo.setProcessingDateTime(DateUtil.convertUTCToEST(new Date(), TimeZone.getTimeZone("UTC"), TimeZone.getTimeZone("America/New_York"))); // 使用persist强制插入 entityManager.persist(tableFlattenProcessingInfo); success = true; } catch (EntityExistsException e) { log.info("Batch already taken up for processing by another Pod -> {}", e.getMessage()); success = false; } catch (Exception e) { log.info("Error Inserting Flatten Batch -> {}", e.getMessage()); success = false; } return success; }
内容的提问来源于stack exchange,提问作者Arul
相关产品推荐
相关产品推荐

