You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,但需确认:

  • 项目中是否存在其他MongoTemplate Bean导致注入错误
  • 在构造函数中打印数据库名称,验证连接的正确性:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:04:52