Micronaut Data JDBC:批量保存条目并更新已存在条目方案咨询
解决Micronaut Data saveAll()遇到唯一约束时的新增/更新需求
针对你用Micronaut/Groovy开发REST API时,遇到的saveAll()因唯一约束冲突失败,需要实现“存在则更新、不存在则新增”(Upsert)的需求,提供两种可行方案:
方案一:利用MySQL原生INSERT ... ON DUPLICATE KEY UPDATE(推荐,性能最优)
这种方式直接借助MySQL的原生语法实现批量Upsert,避免多次数据库交互,适合大数据量场景。
- 确保实体类标记唯一约束
在你的NormalizedValue实体类上,通过@Table注解明确唯一约束字段(替换成你的实际唯一字段,比如code):
@Table(uniqueConstraints = @UniqueConstraint(columnNames = ["code"])) class NormalizedValue { @Id Long id String code // 假设这是唯一约束字段 // 其他字段... }
- 在Repository中自定义批量Upsert方法
修改你的NormalizedRepository,添加使用原生SQL的自定义方法,注意字段要和数据库列名对应:
@Validated @JdbcRepository(dialect= Dialect.MYSQL) interface NormalizedRepository extends CrudRepository<NormalizedValue, Long> { @Transactional @Query(value = """ INSERT INTO normalized_value (code, field1, field2) VALUES (:items.code, :items.field1, :items.field2) ON DUPLICATE KEY UPDATE field1 = VALUES(field1), field2 = VALUES(field2) """, nativeQuery = true) void batchUpsert(@Param("items") List<NormalizedValue> normalizedValues) }
注意:把
code、field1、field2替换成你实体类对应的数据库列名,所有需要更新的字段都要在UPDATE后声明。
- 修改Service层调用逻辑
把原来调用saveAll()的代码换成自定义的batchUpsert():
@Transactional void saveNormalized(List<NormalizedValue> normalizedValues){ normalizedRepository.batchUpsert(normalizedValues) }
方案二:先查询再分批次处理(直观但性能稍差)
如果不想写原生SQL,可以先查询数据库中已存在的条目,再拆分新增和更新两组分别处理,适合数据量较小的场景。
- 在Repository中添加批量查询方法
根据你的唯一约束字段,添加批量查询方法:
@Validated @JdbcRepository(dialect= Dialect.MYSQL) interface NormalizedRepository extends CrudRepository<NormalizedValue, Long> { // 假设唯一约束字段是code,根据批量code查询已存在的条目 List<NormalizedValue> findByCodeIn(List<String> codes) }
- 修改Service层的处理逻辑
先筛选出已存在和不存在的条目,分别执行更新和新增:
@Transactional void saveNormalized(List<NormalizedValue> normalizedValues){ // 提取所有待处理条目的唯一字段值 def allUniqueCodes = normalizedValues.collect { it.code } // 查询数据库中已存在的条目 def existingItems = normalizedRepository.findByCodeIn(allUniqueCodes) def existingMap = existingItems.collectEntries { [it.code, it] } def toUpdate = [] def toInsert = [] normalizedValues.each { item -> def existing = existingMap[item.code] if (existing) { // 将新值覆盖到已存在的实体上 existing.field1 = item.field1 existing.field2 = item.field2 toUpdate << existing } else { toInsert << item } } // 执行批量新增和更新 if (!toInsert.isEmpty()) { normalizedRepository.saveAll(toInsert) } if (!toUpdate.isEmpty()) { normalizedRepository.updateAll(toUpdate) } }
注意:两种方案都需要保留
@Transactional注解,确保整个操作的原子性,避免部分成功部分失败的情况。
内容的提问来源于stack exchange,提问作者AK17396243
相关产品推荐
相关产品推荐

