Couchbase 交易
Couchbase 支持分布式事务。本节记录了如何将其与 Spring Data Couchbase 一起使用。
要求
-
Couchbase Server 6.6.1 或更高版本。
-
Spring Data Couchbase 5.0.0-M5 或更高版本。
-
应配置 NTP,以便 Couchbase 集群的节点与时间同步。不同步的时间不会导致错误的行为,但可能会影响元数据清理。
-
实体类必须具有一个属性来保存文档的 CAS 值。
@Version Long
概述
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;
@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)));
}
}
@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 方法注释的功能需要
-
要用 @EnableTransactionManagement 注释的 Configuration 类;
-
具有 Comments 方法的 Service 对象必须使用 @Service 进行 Comments;
-
方法的主体在事务中执行。
-
必须通过 @Autowired 获取带有 Comments 方法的 Service 对象。
-
对该方法的调用必须从与 Service 不同的类进行,因为调用 Annotated method 不会调用执行事务处理的 Method Interceptor。
使用 CouchbaseTransactionalOperator 的事务
CouchbaseTransactionalOperator可用于内联构造事务,而无需创建使用 @Transactional的服务类。 CouchbaseTransactionalOperator可以作为 bean 使用,并且可以用 @Autowired实例化。 如果显式创建一个,则必须使用 CouchbaseTransactionalOperator.create(manager) (不是 TransactionalOperator.create(manager)) 创建它。
@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 事务的最直接方法。
请参阅参考文档
@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"));
});
@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")))
);