此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Couchbase 5.4.4

Couchbase 交易

Couchbase 支持分布式事务。本节记录了如何将其与 Spring Data Couchbase 一起使用。

要求

概述

Spring Data Couchbase 模板作 insert、find、replace 和 delete 以及使用这些调用的存储库方法可以参与 Couchbase Transaction。它们可以使用 @Transactional 注解、CouchbaseTransactionalOperator 在事务中执行,也可以在 Couchbase Transaction 的 lambda 中执行。

入门和配置

Couchbase 交易通常使用带有 @Transactional 注解的方法进行杠杆处理。 @Transactional运算符是通过CouchbaseTransactionManager实现的,该Manager在AbstractCouchbaseConfiguration中作为 bean 提供。 通过使用 CouchbaseTransactionOperator,可以在不定义服务类的情况下使用 Couchbase Transactions,CouchbaseTransactionOperator 也在 AbtractCouchbaseConfiguration 中作为 bean 提供。 Couchbase 事务也可以使用 lambda 中的 Spring Data Couchbase作直接使用。使用事务

使用 @Transactional 的事务

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

在类级别声明此 Comments 时,它将作为默认值应用 添加到声明类及其子类的所有方法中。

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

在此版本中,Couchbase 事务忽略了 rollback 属性。 事务隔离级别是 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)));
  }
}
事务性服务类

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

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)

Functioning of the @Transactional method annotation requires

  1. the configuration class to be annotated with @EnableTransactionManagement;

  2. the service object with the annotated methods must be annotated with @Service;

  3. the body of the method is executed in a transaction.

  4. the service object with the annotated methods must be obtained via @Autowired.

  5. the call to the method must be made from a different class than service because calling an annotated method from the same class will not invoke the Method Interceptor that does the transaction processing.

Transactions with CouchbaseTransactionalOperator

CouchbaseTransactionalOperator can be used to construct a transaction in-line without creating a service class that uses @Transactional. CouchbaseTransactionalOperator is available as a bean and can be instantiated with @Autowired. If creating one explicitly, it must be created with CouchbaseTransactionalOperator.create(manager) (NOT 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 works seamlessly with the Couchbase Java SDK for transaction processing. Spring Data Couchbase operations that can be executed in a transaction will work directly within the lambda of a transactions().run() without involving any of the Spring Transactions mechanisms. This is the most straight-forward way to leverage Couchbase Transactions in Spring Data 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")))
  );

APP信息