1. 概述

从 MongoDB 4.0 开始,官方正式支持多文档 ACID 事务。与此同时,Spring Data 的 Lovelace 版本(即 Spring Data MongoDB 2.x)也提供了对 MongoDB 原生事务的支持。

在本篇文章中,我们将深入探讨 Spring Data MongoDB 对同步和响应式事务的支持,并介绍如何使用 Spring 的 TransactionTemplate 来实现非原生事务。

如果你对 Spring Data MongoDB 还不熟悉,可以先参考我们的 Spring Data MongoDB 入门指南

2. 配置 MongoDB 4.0 环境

要体验 MongoDB 的事务功能,首先需要安装并运行 MongoDB 4.0 或以上版本。

你可以从 MongoDB 官方下载中心 获取最新版本。

启动 MongoDB 时,需要以副本集模式运行:

mongod --replSet rs0

然后初始化副本集(如果尚未初始化):

mongo --eval "rs.initiate()"

⚠️ 注意:目前 MongoDB 的事务功能仅支持副本集模式,单节点模式不支持。

3. Maven 依赖配置

要在项目中使用 Spring Data MongoDB 的事务功能,需要添加以下依赖:

<dependency>    
    <groupId>org.springframework.data</groupId>    
    <artifactId>spring-boot-starter-data-mongodb</artifactId>    
    <version>2.7.11</version>    
</dependency>

你可以在 Maven 中央仓库 查找最新版本。

4. MongoDB 配置类

配置 MongoDB 事务支持的关键在于注册 MongoTransactionManager

@Configuration
@EnableMongoRepositories(basePackages = "com.baeldung.repository")
public class MongoConfig extends AbstractMongoClientConfiguration{

    @Bean
    MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
        return new MongoTransactionManager(dbFactory);
    }

    @Override
    protected String getDatabaseName() {
        return "test";
    }

    @Override
    public MongoClient mongoClient() {
        final ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
        final MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
            .applyConnectionString(connectionString)
            .build();
        return MongoClients.create(mongoClientSettings);
    }
}

要点:必须显式注册 MongoTransactionManager,否则事务功能不会生效(默认是关闭的)。

5. 同步事务支持

配置完成后,使用事务就非常简单了:只需在方法上添加 @Transactional 注解,方法体内的所有操作就会被包裹在一个事务中执行。

@Test
@Transactional
public void whenPerformMongoTransaction_thenSuccess() {
    userRepository.save(new User("John", 30));
    userRepository.save(new User("Ringo", 35));
    Query query = new Query().addCriteria(Criteria.where("name").is("John"));
    List<User> users = mongoTemplate.find(query, User.class);

    assertThat(users.size(), is(1));
}

⚠️ 注意:某些命令在事务中是不被支持的,例如 listCollections。以下代码会抛出 MongoTransactionException

@Test(expected = MongoTransactionException.class)
@Transactional
public void whenListCollectionDuringMongoTransaction_thenException() {
    if (mongoTemplate.collectionExists(User.class)) {
        mongoTemplate.save(new User("John", 30));
        mongoTemplate.save(new User("Ringo", 35));
    }
}

6. TransactionTemplate 非原生事务

除了使用注解方式,Spring Data 也支持通过 TransactionTemplate 实现非原生事务控制。

这种方式更灵活,适合手动控制事务边界:

@Test
public void givenTransactionTemplate_whenPerformTransaction_thenSuccess() {
    mongoTemplate.setSessionSynchronization(SessionSynchronization.ALWAYS);                                     

    TransactionTemplate transactionTemplate = new TransactionTemplate(mongoTransactionManager);
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            mongoTemplate.insert(new User("Kim", 20));
            mongoTemplate.insert(new User("Jack", 45));
        };
    });

    Query query = new Query().addCriteria(Criteria.where("name").is("Jack")); 
    List<User> users = mongoTemplate.find(query, User.class);

    assertThat(users.size(), is(1));
}

关键点:使用 TransactionTemplate 时,必须将 SessionSynchronization 设置为 ALWAYS

7. 响应式事务支持

Spring Data MongoDB 同样支持响应式编程模型下的事务操作。

首先,添加响应式 MongoDB 的依赖:

<dependency>    
    <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>    
    <version>2.7.11</version>    
</dependency>

以及响应式测试依赖:

<dependency>    
    <groupId>io.projectreactor</groupId>    
    <artifactId>reactor-test</artifactId>    
    <version>3.5.4</version>    
    <scope>test</scope>    
</dependency>

响应式 MongoDB 的配置如下:

@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.baeldung.reactive.repository")
public class MongoReactiveConfig extends AbstractReactiveMongoConfiguration {

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        return "reactive";
    }

    @Bean
    public TransactionalOperator transactionalOperator(
      ReactiveTransactionManager reactiveTransactionManager) {
        return TransactionalOperator.create(reactiveTransactionManager);
    }
}

在响应式流中使用事务的方式如下:

@Autowired
private TransactionalOperator transactionalOperator;

@Autowired
private ReactiveMongoTemplate mongoTemplate;

@Test
public void whenPerformTransaction_thenSuccess() {
    User user1 = new User("Jane", 23);
    User user2 = new User("John", 34);

    Mono<User> saveEntity1 = mongoTemplate.save(user1);
    Mono<User> saveEntity2 = mongoTemplate.save(user2);

    saveEntity1.then(saveEntity2).then().as(transactionalOperator::transactional);
}

更多响应式 MongoDB 的内容,可以参考我们的 响应式 MongoDB 教程

8. 总结

本文我们介绍了如何在 Spring Data MongoDB 中使用:

  • 原生事务(通过 @Transactional
  • 非原生事务(通过 TransactionTemplate
  • 响应式事务(通过 TransactionalOperator

完整代码示例可以在 GitHub 项目 中获取。


原始标题:Spring Data MongoDB Transactions