@transactional对mongodb生效需满足五条件:spring boot≥2.2.0、mongodb为副本集、连接字符串含replicaset参数、显式配置mongotransactionmanager bean、事务内统一使用同一mongotemplate实例。

@Transactional 注解对 MongoDB 生效的前提,不是加了注解就自动事务——它必须搭配正确配置的 MongoTransactionManager,且整个链路满足 5 个硬性条件:Spring Boot ≥2.2.0、MongoDB 是副本集(非单机)、连接字符串含 replicaSet 参数、显式声明 MongoTransactionManager Bean、所有操作用同一个 MongoTemplate 实例。
为什么 @Transactional 对 MongoDB 没反应?
最常见的情况是:代码里写了 @Transactional,但事务根本没开启,数据照样直接写入。这不是 bug,是配置缺失或环境不达标。
典型表现包括:
- 方法抛出异常,数据库记录仍存在(未回滚)
- 日志里完全看不到
Starting transaction或Committing transaction - 用
MongoRepository调用save()或delete(),事务也不生效
根本原因往往是以下之一:
-
spring-boot-starter-data-mongodb版本 @Transactional 对 MongoDB 完全静默忽略 - MongoDB 是 standalone 模式,没启 replica set;或连接串漏了
?replicaSet=rs0 - 没在配置类中定义
MongoTransactionManagerBean,只加了@EnableTransactionManagement也没用 - 业务方法里混用了多个
MongoTemplate(比如一个 autowired 的,另一个 new 出来的),事务上下文无法绑定
如何正确定义 MongoTransactionManager Bean
Spring Boot 不会自动为你配这个 Bean,哪怕你用了最新版 starter。必须手动声明,且注意参数类型。
推荐写法(适配 Spring Data MongoDB 3.0+ 和 Spring Boot 2.4+):
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory databaseFactory) {
return new MongoTransactionManager(databaseFactory);
}
关键点:
- 参数必须是
MongoDatabaseFactory,不是旧版的MongoDbFactory或MongoTemplate - 如果项目同时用了 JPA,必须给两个事务管理器命名,否则
@Transactional默认走DataSourceTransactionManager:@Bean("mongoTransactionManager")
并在方法上指定:@Transactional(transactionManager = "mongoTransactionManager") - 这个 Bean 必须和你的
MongoTemplate共享同一个MongoDatabaseFactory实例,否则事务隔离失效
事务内只能用 MongoTemplate,不能用 MongoRepository
MongoRepository 接口背后默认不参与 Spring 的事务同步机制,即使你把它注入到 @Transactional 方法里,它的操作也不会被纳入当前事务。
实测现象:
- 方法里先调
userRepository.save(),再调mongoTemplate.update(),前者会立即提交,后者才进事务 - 整个方法抛异常,
userRepository.save()的数据不会回滚
解决办法只有两个:
- 统一改用
MongoTemplate:增删改查都走insert()、updateFirst()、remove()等方法 - 或者自定义
MongoRepository实现,强制委托给事务性MongoTemplate(不推荐,破坏抽象)
如果你依赖 QueryByExampleExecutor 或复杂查询,MongoTemplate 同样支持:query(where("status").is("PENDING")) + find()。
事务超时、异常传播与跨库限制
事务看似“开了”,但很多边界情况会让它中途退出或静默失败:
- 默认超时是 60 秒,服务端
transactionLifetimeLimitSeconds配置必须 ≥ 这个值,否则驱动抛MaxTimeMSExpiredException - 异常必须**未被捕获**地抛出方法外,
try-catch里只 log 不 re-throw,事务就当作成功提交 - 所有读写操作必须限定在**同一个数据库内**;跨库(如
db1.users和db2.logs)直接报错:Transactions are not supported across multiple databases - 分片集群下,所有操作必须落在同一分片(靠 shard key 保证),否则事务启动失败,错误藏在
com.mongodb.client.internal.ClientSessionImpl日志里
真正容易被忽略的是:事务开启失败时,Spring 往往不抛异常,只是退化为非事务模式执行——你得盯紧日志里有没有 Could not obtain transaction-synchronized Session 或底层驱动的 session 初始化失败提示。











