跳到主要内容

Couchbase 事务

ChatGPT-4o-mini 中英对照 Couchbase Transactions

Couchbase 支持 分布式事务。本节文档介绍了如何在 Spring Data Couchbase 中使用它。

需求

  • Couchbase Server 6.6.1 或以上版本。

  • Spring Data Couchbase 5.0.0-M5 或以上版本。

  • 应配置 NTP,以确保 Couchbase 集群的节点时间同步。时间不同步不会导致不正确的行为,但可能会影响元数据清理。

  • 实体类必须具有 @Version Long 属性,用于存储文档的 CAS 值。

概述

Spring Data Couchbase 模板操作插入、查找、替换和删除,以及使用这些调用的存储库方法,可以参与 Couchbase 事务。它们可以通过使用 @Transactional 注解、CouchbaseTransactionalOperator 或在 Couchbase 事务的 lambda 中执行。

开始使用与配置

Couchbase 事务通常通过带有 @Transactional 注解的方法来实现。 @Transactional 操作符是通过 CouchbaseTransactionManager 实现的,该管理器作为一个 bean 提供在 AbstractCouchbaseConfiguration 中。Couchbase 事务可以通过使用 CouchbaseTransactionOperator 来使用,而无需定义服务类,该操作符同样作为一个 bean 提供在 AbtractCouchbaseConfiguration 中。Couchbase 事务还可以通过在 lambda 中直接使用 Spring Data Couchbase 操作来使用 使用事务

使用 @Transactional 的事务

@Transactional 定义了一个方法或类中的所有方法为事务性方法。

当此注解在类级别声明时,它作为默认值适用于声明类及其子类的所有方法。

[[-attribute-semantics]] === 属性语义

在此版本中,Couchbase 事务忽略了回滚属性。事务隔离级别为已提交读取(read-committed);

示例 1. 通过 @Transactional 进行事务配置和使用

@Configuration
@EnableCouchbaseRepositories("<parent-dir-of-repository-interfaces>")
@EnableReactiveCouchbaseRepositories("<parent-dir-of-repository-interfaces>")
@EnableTransactionManagement 1
static class Config extends AbstractCouchbaseConfiguration {

// Usual Setup
@Override public String getConnectionString() { /* ... */ }
@Override public String getUserName() { /* ... */ }
@Override public String getPassword() { /* ... */ }
@Override public String getBucketName() { /* ... */ }

// Customization of transaction behavior is via the configureEnvironment() method
@Override protected void configureEnvironment(final Builder builder) {
builder.transactionsConfig(
TransactionsConfig.builder().timeout(Duration.ofSeconds(30)));
}
}
java

事务服务类

请注意,如果事务失败,@Transactional 方法的主体可以被重新执行。方法主体中的所有内容都必须是幂等的。

import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

final CouchbaseOperations personOperations;
final ReactiveCouchbaseOperations reactivePersonOperations;

@Service 2
public class PersonService {

final CouchbaseOperations operations;
final ReactiveCouchbaseOperations reactiveOperations;

public PersonService(CouchbaseOperations ops, ReactiveCouchbaseOperations reactiveOps) {
operations = ops;
reactiveOperations = reactiveOps;
}

// no annotation results in this method being executed not in a transaction
public Person save(Person p) {
return operations.save(p);
}

@Transactional
public Person changeFirstName(String id, String newFirstName) {
Person p = operations.findById(Person.class).one(id); 3
return operations.replaceById(Person.class).one(p.withFirstName(newFirstName);
}

@Transactional
public Mono<Person> reactiveChangeFirstName(String id, String newFirstName) {
return personOperationsRx.findById(Person.class).one(person.id())
.flatMap(p -> personOperationsRx.replaceById(Person.class).one(p.withFirstName(newFirstName)));
}

}
java
@Autowired PersonService personService; 4

Person walterWhite = new Person( "Walter", "White");
Person p = personService.save(walterWhite); // this is not a transactional method
...
Person renamedPerson = personService.changeFirstName(walterWhite.getId(), "Ricky"); 5
java

@Transactional 方法注解的功能要求

  1. 配置类需要使用 @EnableTransactionManagement 注解。

  2. 带有注解方法的服务对象必须使用 @Service 注解。

  3. 方法的主体是在事务中执行的。

  4. 带有注解方法的服务对象必须通过 @Autowired 获取。

  5. 方法的调用必须来自与服务不同的类,因为从同一类中调用带有注解的方法不会触发处理事务的 Method Interceptor。

使用 CouchbaseTransactionalOperator 的事务

CouchbaseTransactionalOperator 可以用来在线构建事务,而无需创建一个使用 @Transactional 的服务类。CouchbaseTransactionalOperator 作为一个 bean 可用,并且可以通过 @Autowired 注入。如果显式创建一个,它必须通过 CouchbaseTransactionalOperator.create(manager) 创建(而不是 TransactionalOperator.create(manager))。

示例 2. 使用 TransactionalOperator.execute() 进行事务访问

@Autowired TransactionalOperator txOperator;
@Autowired ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;

Flux<Person> result = txOperator.execute((ctx) ->
reactiveCouchbaseTemplate.findById(Person.class).one(person.id())
.flatMap(p -> reactiveCouchbaseTemplate.replaceById(Person.class).one(p.withFirstName("Walt")))
);
java

直接使用 SDK 进行交易

Spring Data Couchbase 与 Couchbase Java SDK 在事务处理方面无缝协作。可以在 transactions().run() 的 lambda 中直接执行的 Spring Data Couchbase 操作将直接工作,而无需涉及任何 Spring 事务机制。这是利用 Spring Data Couchbase 中 Couchbase 事务的最简单方法。

请参阅参考文档

示例 3. 事务访问 - 阻塞

@Autowired CouchbaseTemplate couchbaseTemplate;

TransactionResult result = couchbaseTemplate.getCouchbaseClientFactory().getCluster().transactions().run(ctx -> {
Person p = couchbaseTemplate.findById(Person.class).one(personId);
couchbaseTemplate.replaceById(Person.class).one(p.withFirstName("Walt"));
});
java

示例 4. 事务访问 - 响应式

@Autowired ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;

Mono<TransactionResult> result = reactiveCouchbaseTemplate.getCouchbaseClientFactory().getCluster().reactive().transactions()
.run(ctx ->
reactiveCouchbaseTemplate.findById(Person.class).one(personId)
.flatMap(p -> reactiveCouchbaseTemplate.replaceById(Person.class).one(p.withFirstName("Walt")))
);
java