此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Couchbase 5.4.4! |
Couchbase 交易
Couchbase 支持分布式事务。本节记录了如何将其与 Spring Data Couchbase 一起使用。
要求
-
Couchbase Server 6.6.1 或更高版本。
-
Spring Data Couchbase 5.0.0-M5 或更高版本。
-
应配置 NTP,以便 Couchbase 集群的节点与时间同步。不同步的时间不会导致错误的行为,但可能会影响元数据清理。
-
实体类必须具有
@Version Long
属性来保存文档的 CAS 值。
概述
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)));
}
}
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
-
the configuration class to be annotated with @EnableTransactionManagement;
-
the service object with the annotated methods must be annotated with @Service;
-
the body of the method is executed in a transaction.
-
the service object with the annotated methods must be obtained via @Autowired.
-
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.
Please see the Reference Documentation
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")))
);