对于最新稳定版本,请使用 Spring Data Couchbase 6.0.4spring-doc.cadn.net.cn

Couchbase 事务

Couchbase 支持分布式事务。本节介绍如何在 Spring Data Couchbase 中使用它。spring-doc.cadn.net.cn

要求

概述

Spring Data Couchbase 模板操作(包括 insert、find、replace 和 delete)以及使用这些调用的仓库方法可以参与 Couchbase 事务。可以通过使用 @Transactional 注解、CouchbaseTransactionalOperator,或在 Couchbase 事务的 lambda 表达式中来执行事务。spring-doc.cadn.net.cn

入门与配置

Couchbase 事务通常通过与 @Transactional 注解的方法配合使用。 @Transactional 操作符由 CouchbaseTransactionManager 实现,该管理器作为 Bean 在 AbstractCouchbaseConfiguration 中提供。 Couchbase 事务也可在不定义服务类的情况下,通过使用同样作为 Bean 在 AbtractCouchbaseConfiguration 中提供的 CouchbaseTransactionOperator 来使用。 Couchbase 事务还可以通过在 lambda 表达式内直接使用 Spring Data Couchbase 操作来使用,详见 使用事务spring-doc.cadn.net.cn

使用 @Transactional 的事务处理

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

当此注解在类级别声明时,它作为默认值应用于声明类及其所有子类的所有方法。spring-doc.cadn.net.cn

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

在此版本中,Couchbase 事务忽略回滚属性。 事务隔离级别为已提交读;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 方法的主体可能会被重新执行。 因此,方法体中的所有内容都必须是幂等的。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)));
  }

}
使用 @Transactional 服务。
@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 方法注解需要spring-doc.cadn.net.cn

  1. 要使用 @EnableTransactionManagement 进行注解的配置类;spring-doc.cadn.net.cn

  2. 具有注解方法的服务对象必须使用@Service进行注解;spring-doc.cadn.net.cn

  3. 方法体在事务中执行。spring-doc.cadn.net.cn

  4. 必须通过 @Autowired 获取带有注解的方法的服务对象。spring-doc.cadn.net.cn

  5. 必须从不同于 service 的类中调用该方法,因为从同一类中调用带注解的方法将不会触发执行事务处理的 Method Interceptor。spring-doc.cadn.net.cn

CouchbaseTransactionalOperator 的事务处理

CouchbaseTransactionalOperator 可用于在不创建使用 @Transactional 的服务类的情况下内联构建事务。 CouchbaseTransactionalOperator 作为 Bean 可用,并可通过 @Autowired 进行实例化。 如果显式创建一个,则必须使用 CouchbaseTransactionalOperator.create(manager) 创建(而非 TransactionalOperator.create(manager))。spring-doc.cadn.net.cn

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

通过 SDK 直接进行交易

Spring Data Couchbase 与 Couchbase Java SDK 无缝协作以进行事务处理。可在事务中执行的 Spring Data Couchbase 操作将直接在 transactions().run() 的 lambda 内部运行,无需涉及任何 Spring 事务机制。这是在 Spring Data Couchbase 中利用 Couchbase 事务的最直接方式。spring-doc.cadn.net.cn

示例 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"));
});
示例 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")))
  );