Spring操作MongoDB时新增如何忽略已存在实体仅添加新数据
Spring Data MongoDB 批量插入仅新增实体实现方案
核心逻辑是依赖MongoDB的唯一索引约束,配合插入时的重复错误忽略机制实现,不需要自行实现先查后插逻辑(先查后插存在并发竞态风险,性能也更低)。
方案1:BulkOperations 批量插入(推荐)
适合全量场景,性能最高,并发安全。
前置准备
给实体类的唯一标识字段(主键或业务唯一键)添加唯一索引,这是识别重复数据的前提:
import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.annotation.Id; @Document(collection = "your_collection") public class YourEntity { @Id private String id; // 业务唯一键添加唯一索引,也可以用@CompoundIndex定义联合唯一索引 @Indexed(unique = true) private String businessUniqueKey; // 其他字段、getter/setter省略 }
插入代码实现
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.BulkOperations; import com.mongodb.bulk.BulkWriteResult; import java.util.List; public void batchAddOnlyNewEntities(List<YourEntity> toInsertList) { // 初始化无序批量操作:单条插入失败不影响其他操作,无需保证插入顺序 BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, YourEntity.class); bulkOps.insert(toInsertList); // 执行操作,MongoDB会自动忽略重复键的插入错误,仅插入新实体 BulkWriteResult result = bulkOps.execute(); // 可选:获取成功插入的条数 int insertedCount = result.getInsertedCount(); }
方案2:先过滤再插入(仅适合小数据量场景)
如果数据量很小(百条级别),可以提前过滤掉已存在的实体再插入:
import org.springframework.data.mongodb.repository.MongoRepository; import java.util.stream.Collectors; // 定义Repository public interface YourEntityRepository extends MongoRepository<YourEntity, String> { List<YourEntity> findByBusinessUniqueKeyIn(List<String> keyList); } // 插入逻辑 public void batchAddOnlyNewEntities(List<YourEntity> toInsertList) { // 提取待插入实体的唯一键列表 List<String> toInsertKeys = toInsertList.stream() .map(YourEntity::getBusinessUniqueKey) .collect(Collectors.toList()); // 查询已存在的唯一键 List<String> existedKeys = yourEntityRepository.findByBusinessUniqueKeyIn(toInsertKeys) .stream() .map(YourEntity::getBusinessUniqueKey) .collect(Collectors.toList()); // 过滤出全新的实体 List<YourEntity> newEntities = toInsertList.stream() .filter(entity -> !existedKeys.contains(entity.getBusinessUniqueKey())) .collect(Collectors.toList()); // 批量插入新实体 yourEntityRepository.saveAll(newEntities); }
注意:该方案并发场景下存在竞态风险,若查询后插入前有其他线程插入相同唯一键,仍会触发唯一键冲突报错,不适合高并发或大数据量场景。
常见说明
- 不要使用
upsert实现该需求:upsert会更新已存在的旧数据,不符合需求。 - 唯一索引支持联合索引:使用
@CompoundIndex定义联合唯一索引即可,逻辑和单字段唯一索引完全一致。
内容的提问来源于stack exchange,提问作者platypus
相关产品推荐
相关产品推荐

