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

Couchbase 交易

Couchbase 支持分布式事务。本节记录了如何将其与 Spring Data Couchbase 一起使用。spring-doc.cadn.net.cn

要求

概述

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

入门和配置

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

使用 @Transactional 的事务

@Transactional类中的方法或所有方法定义为事务性方法。spring-doc.cadn.net.cn

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

[[-属性语义]] === 属性语义spring-doc.cadn.net.cn

在此版本中,Couchbase 事务忽略了 rollback 属性。 事务隔离级别是 read-committed;spring-doc.cadn.net.cn

示例 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 都必须是幂等的。spring-doc.cadn.net.cn

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 requiresspring-doc.cadn.net.cn

  1. the configuration class to be annotated with @EnableTransactionManagement;spring-doc.cadn.net.cn

  2. the service object with the annotated methods must be annotated with @Service;spring-doc.cadn.net.cn

  3. the body of the method is executed in a transaction.spring-doc.cadn.net.cn

  4. the service object with the annotated methods must be obtained via @Autowired.spring-doc.cadn.net.cn

  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.spring-doc.cadn.net.cn

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)).spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

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")))
  );