必须手动声明MongoTransactionManager Bean,因为Spring Boot仅自动配置JDBC事务管理器,对MongoDB需显式定义该Bean,否则@Transactional静默失效;参数必须为MongoDatabaseFactory,且需与MongoTemplate共享同一实例,连接串须含replicaSet参数,多数据源时还需命名并显式指定。

为什么必须手动声明 MongoTransactionManager Bean
Spring Boot 不会自动为你配 MongoTransactionManager,哪怕你用了 spring-boot-starter-data-mongodb 3.0+ 和 @EnableTransactionManagement。它只对 JDBC 数据源自动配置事务管理器,MongoDB 的事务管理器始终需要显式声明。没这个 Bean,@Transactional 对 MongoDB 就是静默忽略——不报错、不回滚、日志里连 Starting transaction 都看不到。
常见错误包括:
- 误以为加了
@EnableTransactionManagement就够了 - 用旧版参数类型(如
MongoTemplate或MongoDbFactory)导致编译通过但运行时事务不绑定 - Bean 名未区分,和 JPA 的
DataSourceTransactionManager冲突,结果事务默认走数据库而非 MongoDB
正确写法:参数必须是 MongoDatabaseFactory
Spring Data MongoDB 3.0+(对应 Spring Boot 2.4+)要求构造 MongoTransactionManager 时传入 MongoDatabaseFactory 实例,而不是旧版的 MongoTemplate 或已废弃的 MongoDbFactory。否则事务上下文无法与实际数据库操作关联。
推荐写法:
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory databaseFactory) {
return new MongoTransactionManager(databaseFactory);
}
关键点:
- 参数类型必须是
MongoDatabaseFactory,Spring Boot 自动配置的正是这个实例 - 确保该
MongoDatabaseFactory和你使用的MongoTemplate是同一个——它们默认共享,但如果自定义了多个MongoTemplate,就得确认其背后的 factory 是否一致 - 如果项目同时用了 JPA,必须命名并显式指定:
@Bean("mongoTransactionManager"),并在方法上写@Transactional(transactionManager = "mongoTransactionManager")
连接字符串漏掉 replicaSet 参数会导致 Bean 初始化失败
MongoTransactionManager 的初始化依赖连接串中明确携带 ?replicaSet=xxx。Spring Boot 在解析 URI 时,只有看到这个参数,才会启用副本集模式,并允许事务管理器创建 session。单机模式(standalone)下即使声明了 Bean,启动时也会静默跳过或后续调用直接抛 No transaction in progress。
检查你的 application.properties:
spring.data.mongodb.uri=mongodb://localhost:27017,localhost:27018,localhost:27019/mydb?replicaSet=rs0
注意:
- 地址列表必须包含全部副本集成员(至少一个可连通)
-
?replicaSet=rs0中的rs0必须和 MongoDB 实际初始化的副本集名称完全一致(大小写敏感) - 不要用
localhost:27017/mydb这种单点写法,即使本地测试也得模拟副本集结构
事务内混用 MongoTemplate 实例会让 MongoTransactionManager 失效
MongoTransactionManager 绑定的是 MongoDatabaseFactory 级别的 session,而每个 MongoTemplate 实例背后可能持有独立的 MongoDatabaseFactory(尤其是你手动 new 或 @Qualifier 注入多个 template 时)。一旦事务方法里用了 A template,又在某处偷偷用了 B template,操作就脱离了当前事务上下文。
典型踩坑场景:
- Service 层 autowired 了一个
MongoTemplate,但某个工具类里又new MongoTemplate(...) - 用了
@Primary和@Qualifier("secondaryTemplate"),却在同一个事务方法里交替调用两者 - 通过
MongoTemplate.withCollection()切 collection 没问题,但跨withDatabase("otherdb")就越界了——事务不支持跨库
验证方式:在事务方法开头打日志输出 template.getMongoDatabaseFactory().getClass(),确保所有操作用的 factory 是同一个实例。
事务不是开关,而是上下文绑定;MongoTransactionManager 再准,也管不住你代码里悄悄换掉的 template。

















