@transactional对mongodb多文档事务生效需同时满足5个条件:副本集模式、正确连接字符串、显式声明mongotransactionmanager bean、同一mongotemplate实例、spring boot≥2.2.0;缺一即不回滚。

@Transactional 对 MongoDB 多文档事务生效,不是加了注解就自动起作用——它必须满足 5 个硬性条件,缺一不可。
为什么 @Transactional 方法里写入数据不回滚?
典型现象:方法抛出异常,mongoTemplate.insert() 写入的文档依然存在;日志里完全没出现 Starting transaction 或 Committing transaction;用 MongoRepository.save() 同样无效。
根本原因通常是以下之一:
- MongoDB 实例是 standalone 模式,没启副本集(
rs.initiate()没执行) - 连接字符串漏了
?replicaSet=rs0,比如写成mongodb://localhost:27017/db而不是mongodb://localhost:27017,localhost:27018,localhost:27019/db?replicaSet=rs0 - 没手动声明
MongoTransactionManagerBean(Spring Boot 不会自动配这个,哪怕 URI 正确) - 事务方法内混用了多个
MongoTemplate实例(例如一个@Autowired,另一个用new MongoTemplate(...)) - 项目同时用了 JPA,但没给事务管理器命名,导致
@Transactional默认走DataSourceTransactionManager
如何正确定义 MongoTransactionManager Bean?
必须显式声明,且参数类型只能是 MongoDatabaseFactory(不是旧版的 MongoDbFactory 或 MongoTemplate):
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory databaseFactory) {
return new MongoTransactionManager(databaseFactory);
}
关键点:
- 如果项目同时有 JPA,必须命名并显式指定:
@Bean("mongoTransactionManager"),并在方法上写@Transactional(transactionManager = "mongoTransactionManager") - 该
databaseFactory必须和你的MongoTemplate使用同一个实例,否则事务上下文无法绑定 - Spring Boot ≥ 2.2.0 才支持,低于此版本即使配置正确也静默忽略
@Transactional
事务内操作必须用同一个 MongoTemplate 实例
这是最容易被忽略的隐性坑。事务上下文靠 ThreadLocal 绑定到当前 MongoTemplate 实例,一旦中途切换实例,事务就“断连”了。
常见错误写法:
- 在 service 方法里
@Autowired private MongoTemplate template;,但又在某个工具类里new MongoTemplate(...)并调用 - 使用多个
@Qualifier注入不同命名的MongoTemplate,却没统一事务管理器 - 在事务方法中调用另一个 service 的方法,而那个方法内部用了独立的
MongoTemplate(尤其跨模块时)
安全做法:所有数据操作统一通过同一个 @Autowired MongoTemplate 完成;若需多数据源,务必为每个源配独立命名的 MongoTransactionManager 并显式指定。
单文档原子性 ≠ 多文档事务,别默认开启
MongoDB 单文档更新天然原子,这是 WiredTiger 引擎保证的,不需要事务。多文档事务是另一套机制:依赖副本集协调、多数派写入、writeConcern="majority" 和 readConcern="snapshot",性能开销明显更高。
所以:
- 优先考虑嵌入式文档结构(
address字段直接嵌在user文档里),避免跨集合事务 - 只有真正需要强一致性的多文档场景(如订单+库存+物流三集合联动)才启用事务
- 事务方法不要做耗时操作(如 HTTP 调用、大文件处理),否则容易触发默认 60 秒事务超时
事务不是兜底方案,而是有明确成本的显式契约——配置对了,不代表就应该用。











