Spring MongoTemplate批量Upsert未执行问题排查求助
MongoDB批量Upsert无操作问题排查与解决
文档结构
实体类CustomerRouteManagementReportDocument定义如下:
@Data @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) @Document public class CustomerRouteManagementReportDocument implements TransactionDocument { @Id private String id; @Indexed(unique = true) private String customerId; // ... 其他属性 private List<RouteHistoryEvent> routeHistoryEvents; // ... 其他属性 }
期望功能
实现批量Upsert逻辑:
- 匹配
customerId的文档存在时,将传入对象的routeHistoryEvents首个元素追加到现有文档的对应数组中 - 无匹配项时插入全新的
CustomerRouteManagementReportDocument文档
实现代码
服务层upsertTall方法实现:
@Service @Slf4j public class CustomerRouteManagementReportRepositoryAdapter extends AbstractRepositoryAdapter<CustomerRouteManagementReport, CustomerRouteManagementReportDocument, CustomerRouteManagementReportDataRepository> implements CustomerRouteManagementReportRepository { private final MongoTemplate mongoTemplate; @Autowired public CustomerRouteManagementReportRepositoryAdapter(ModelMapper mapper, CustomerRouteManagementReportDataRepository repository, MongoTemplate mongoTemplate) { super(mapper, repository, CustomerRouteManagementReport.class, CustomerRouteManagementReportDocument.class); this.mongoTemplate = mongoTemplate; } //... 其他方法 @Override public Integer upsertTall(List<CustomerRouteManagementReportDocument> customerRouteManagementReports) { BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.ORDERED, CustomerRouteManagementReportDocument.class); try { List<Pair<Query, Update>> updates = new ArrayList<>(); customerRouteManagementReports.stream().forEach(customerRouteManagementReport -> { Query query = Query.query(Criteria.where("customerId").is(customerRouteManagementReport.getCustomerId())); Update update = new Update(); update.addToSet("routeHistoryEvents").value(customerRouteManagementReport.getRouteHistoryEvents().get(0)); updates.add(Pair.of(query, update)); }); bulkOps.upsert(updates); BulkWriteResult result = bulkOps.execute(); log.info("== BULK RESULT: {}, INSERTED: {}", result, result.getInsertedCount()); return result.getInsertedCount(); } catch (Exception e) { log.error("ERROR {}: IN UPSERTION {}", e.getClass().toGenericString(), e.getMessage()); return 0; } } }
问题现象
执行后既无预期的数组追加也无新文档插入,无异常抛出。日志输出:
== BULK RESULT: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=4, removedCount=0, modifiedCount=4, upserts=[]}, INSERTED: 0
调试确认方法被正常调用,生成了对应数量的Query-Update对,但无MongoTemplate操作日志输出。
MongoDB配置类
@Configuration @ConditionalOnProperty(name = "spring.data.mongodb.uri-report") @EnableReactiveMongoRepositories(basePackages = "package name", reactiveMongoTemplateRef = ReportMongoConfig.REPORT_REACTIVE_MONGO_TEMPLATE) public class ReportMongoConfig { protected static final String REPORT_REACTIVE_MONGO_TEMPLATE = "reportReactiveMongoTemplate"; @Value("${spring.data.mongodb.uri-report}") private String connectionStringReport; @Bean public ConnectionString getConnectionDataBaseReport() { return new ConnectionString(connectionStringReport); } @Bean public ReactiveMongoTemplate reportReactiveMongoTemplate(MappingMongoConverter mappingMongoConverter) { return new ReactiveMongoTemplate(reactiveReportMongoDatabaseFactory(getConnectionDataBaseReport()), mappingMongoConverter); } @Bean public MongoTemplate mongoTemplate(MappingMongoConverter mappingMongoConverter) { return new MongoTemplate(reportMongoDBFactory(getConnectionDataBaseReport()), mappingMongoConverter); } @Bean public ReactiveMongoDatabaseFactory reactiveReportMongoDatabaseFactory(ConnectionString getConnectionDataBaseExperience) { return new SimpleReactiveMongoDatabaseFactory(getConnectionDataBaseExperience); } @Bean public MongoDbFactory reportMongoDBFactory(ConnectionString connectionString) { return new SimpleMongoClientDbFactory(connectionString); } }
排查与解决步骤
1. 修正addToSet的行为问题
update.addToSet仅当元素不存在于数组中时才会追加,若传入的RouteHistoryEvent已存在于目标数组,不会触发修改。如果需要强制追加元素,替换为push:
update.push("routeHistoryEvents").value(customerRouteManagementReport.getRouteHistoryEvents().get(0));
2. 完善Upsert的插入逻辑
当前Update仅定义了数组追加操作,无匹配文档时,MongoDB会插入仅包含customerId和单个routeHistoryEvents元素的极简文档,而非完整的CustomerRouteManagementReportDocument。若需插入完整文档,有两种方案:
- 方案一:先查询匹配文档,存在则执行Update,不存在则执行Insert
- 方案二:使用
setOnInsert补充插入时需要设置的其他字段:
Update update = new Update(); update.push("routeHistoryEvents").value(customerRouteManagementReport.getRouteHistoryEvents().get(0)); // 插入时设置其他必填字段 update.setOnInsert("id", customerRouteManagementReport.getId()); update.setOnInsert("xxx", customerRouteManagementReport.getXxx()); // 替换为实际属性
3. 验证MongoTemplate注入正确性
配置类中定义了MongoTemplate Bean,但需确认:
- 项目中是否存在其他
MongoTemplateBean导致注入错误 - 在构造函数中打印数据库名称,验证连接的正确性:
public CustomerRouteManagementReportRepositoryAdapter(ModelMapper mapper, CustomerRouteManagementReportDataRepository repository, MongoTemplate mongoTemplate) { super(mapper, repository, CustomerRouteManagementReport.class, CustomerRouteManagementReportDocument.class); this.mongoTemplate = mongoTemplate; log.info("Connected MongoDB database: {}", mongoTemplate.getDb().getName()); }
4. 开启MongoTemplate日志
在application.yml中添加日志配置,查看具体的MongoDB操作语句,定位问题:
logging: level: org.springframework.data.mongodb.core: DEBUG
5. 检查customerId索引与查询匹配
- 通过MongoDB客户端查看集合索引,确认
customerId的唯一索引是否正确创建 - 调试时打印
customerRouteManagementReport.getCustomerId()的值,验证查询条件是否准确
内容的提问来源于stack exchange,提问作者LeperAffinity666
相关产品推荐
相关产品推荐

