Couchbase Transactions

Couchbase 支持 ``。本部分记录了如何将它与 Spring Data Couchbase 配合使用。

Requirements

  • Couchbase Server 6.6.1 或更高版本。

  • Spring Data Couchbase 5.0.0-M5 或更高版本。

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

  • 实体类必须具有 @Version Long 属性来保存文档的 CAS 值。

Overview

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

Getting Started & Configuration

Couchbase 事务通常利用带有 注释的方法。 操作符利用 CouchbaseTransactionManager 实现,它在 AbstractCouchbaseConfiguration 中提供为 bean。Couchbase 事务可以通过使用 CouchbaseTransactionOperator(它也在 AbtractCouchbaseConfiguration 中提供为 bean)而无需定义服务类。Couchbase 事务还可以使用 lambda `` 中的 Spring Data Couchbase 操作直接使用。

Transactions with @Transactional

@Transactional 将方法或类上的所有方法定义为事务。

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

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

在此版本中,Couchbase 事务将忽略回滚属性。事务隔离级别已读取提交;

Example 1. Transaction Configuration and Use by @Transactional
The Configuration
@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)));
  }
}
The Transactional Service Class

请注意,如果事务失败,则可以重新执行 @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)));
  }

}
Using the @Transactional Service.
@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)

@Transactional 方法注释的功能需要

  1. 使用 @EnableTransactionManagement 注释的配置类;

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

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

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

  5. 必须从不同于服务的类调用该方法,因为从同一类调 用注释方法不会调用执行事务处理的方法拦截器。

Transactions with CouchbaseTransactionalOperator

CouchbaseTransactionalOperator 可用于构建事务,而不必创建使用 @Transactional 的服务类。CouchbaseTransactionalOperator 可用作 Bean,并可以用 @Autowired 实例化。如果明确创建一个,则必须使用 CouchbaseTransactionalOperator.create(manager) 创建(而不是 TransactionalOperator.create(manager))。

Example 2. Transaction Access Using 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")))
 );

Transactions Directly with the SDK

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

请参阅 ``。

Example 3. Transaction Access - Blocking
@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"));
});
Example 4. Transaction Access - Reactive
@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")))
  );