SpringBoot集成Mongock启动报错:多语句事务内不允许writeConcern
SpringBoot集成Mongock为数据库添加索引时,启动应用抛出如下异常:
Caused by: io.mongock.api.exception.MongockException: io.mongock.api.exception.MongockException: Error in method[AddIndexToCoaching.addPartialIndexToCoaching] : Command failed with error 72 (InvalidOptions): 'writeConcern is not allowed within a multi-statement transaction' on server my-mongo-clus-shard-00-02.s5go0.mongodb.net:27017. The full response is {"ok": 0.0, "errmsg": "writeConcern is not allowed within a multi-statement transaction", "code": 72, "codeName": "InvalidOptions", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1724994211, "i": 24}}, "signature": {"hash": {"$binary": {"base64": "39UxbjF63gosdF8ktyThibybXBQ=", "subType": "00"}}, "keyId": 7357981548798279686}}, "operationTime": {"$timestamp": {"t": 1724994211, "i": 24}}}
at io.mongock.driver.mongodb.springdata.v4.SpringDataMongoV4DriverBase.executeInTransaction(SpringDataMongoV4DriverBase.java:99)
at io.mongock.runner.core.executor.ChangeExecutorBase.processChangeLogInTransactionIfApplies(ChangeExecutorBase.java:147)
at io.mongock.runner.core.executor.ChangeExecutorBase.processSingleChangeLog(ChangeExecutorBase.java:119)
使用的MongoDB连接URL:
mongodb+srv://user:<password>@my-mongo-clus.s5go0.mongodb.net/dbname?retryWrites=true&w=majority
对应的ChangeUnit代码:
@ChangeUnit(id="addPartialIndexToCoaching", order = "0001", author = "user1") @Slf4j public class AddIndexToCoaching { @Execution public void addPartialIndexToCoaching(MongoTemplate mongoTemplate) { // Create the index fields and sorting Document indexFields = new Document() .append("field1", 1) .append("field2", 1) .append("field3", 1); // Define the partial filter expression using Criteria Document partialFilterExpression = Criteria.where("status").is("DRAFT").getCriteriaObject(); // Create the IndexOptions with the partial filter expression IndexOptions options = new IndexOptions().partialFilterExpression(partialFilterExpression); // Create the index using MongoTemplate mongoTemplate.getCollection("mycollection").createIndex(indexFields, options); } @RollbackExecution @RollbackBeforeExecution public void rollback() { } }
主应用启用的注解:
@EnableKafka @EnableMongoRepositories(basePackages = { "my.backend.da.repository"}) @EnableMongoAuditing @EnableAuthorization @EnableAsync(proxyTargetClass = true) @EnableScheduling @EnableCaching @EnableRetry @EnableMongock @EnableSchedulerLock(defaultLockAtMostFor = "${shedlock.defaultLockAtMostFor}") @EnableTransactionManagement @EnableCronMonitoring public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
核心原因
报错的本质是:Mongock默认会将ChangeUnit的执行逻辑包裹在多语句事务中,而你的MongoDB连接URL指定了w=majority的写关注,MongoDB不允许在多语句事务内显式设置writeConcern。同时,直接调用mongoTemplate.getCollection().createIndex()会绕过Spring Data的事务管理,和Mongock的事务上下文产生冲突。
解决步骤
1. 改用Spring Data规范的索引创建方式
替换直接操作Collection的代码,使用MongoTemplate.indexOps()方法创建索引,该方法能自动适配Mongock的事务上下文:
@Execution public void addPartialIndexToCoaching(MongoTemplate mongoTemplate) { // 定义复合索引的字段 IndexField field1 = IndexField.asc("field1"); IndexField field2 = IndexField.asc("field2"); IndexField field3 = IndexField.asc("field3"); CompoundIndexDefinition indexDefinition = new CompoundIndexDefinition(Arrays.asList(field1, field2, field3)); // 设置部分索引过滤条件 Document partialFilter = Criteria.where("status").is("DRAFT").getCriteriaObject(); indexDefinition.withPartialFilterExpression(partialFilter); // 确保索引存在(不存在则创建) mongoTemplate.indexOps("mycollection").ensureIndex(indexDefinition); }
2. 为当前ChangeUnit禁用事务(备选方案)
如果上述方法仍未解决问题,可以直接在@ChangeUnit注解中禁用事务——因为创建索引操作本身不需要事务支持:
@ChangeUnit(id="addPartialIndexToCoaching", order = "0001", author = "user1", transactional = false)
3. 调整MongoDB连接URL(不推荐生产环境随意修改)
若业务允许,可以尝试移除URL中的&w=majority参数,但该方法会影响全局写关注级别,需谨慎评估。
内容的提问来源于stack exchange,提问作者vallish

