Couchbase トランザクション

Couchbase は分散トランザクション (英語) をサポートします。このセクションでは、Spring Data Couchbase での使用方法について説明します。

要件

  • Couchbase サーバー 6.6.1 以降。

  • Spring Data Couchbase 5.0.0-M5 以上。

  • Couchbase クラスターのノードが時刻と同期するように NTP を構成する必要があります。時刻が同期していなくても不正な動作が発生することはありませんが、メタデータのクリーンアップに影響を与える可能性があります。

  • エンティティクラスには、ドキュメントの CAS 値を保持する @Version Long プロパティが必要です。

概要

Spring Data Couchbase テンプレート操作の挿入、検索、置換、削除と、それらの呼び出しを使用するリポジトリメソッドは Couchbase トランザクションに参加できます。これらは、@Transactional アノテーション、CouchbaseTransactionalOperator を使用してトランザクション内で実行するか、Couchbase トランザクションのラムダ内で実行できます。

はじめにと設定

Couchbase トランザクションは通常、@Transactional のアノテーションが付けられたメソッドを使用して利用されます。@Transactional オペレーターは、AbstractCouchbaseConfiguration の Bean として提供される CouchbaseTransactionManager で実装されます。Couchbase トランザクションは、AbtractCouchbaseConfiguration の Bean としても提供される CouchbaseTransactionOperator を使用することで、サービスクラスを定義せずに使用できます。Couchbase トランザクションは、ラムダトランザクションの使用 (英語) 内で Spring Data Couchbase 操作を使用して直接使用することもできます。

@Transactional との取引

@Transactional は、クラス上の 1 つのメソッドまたはすべてのメソッドをトランザクションとして定義します。

このアノテーションがクラスレベルで宣言されると、宣言したクラスとそのサブクラスのすべてのメソッドにデフォルトとして適用されます。

[[-attribute-semantics]] === 属性セマンティクス

このリリースでは、Couchbase トランザクションはロールバック属性を無視します。トランザクション分離レベルは読み取りコミットです。

例 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 メソッドの本体は再実行できることに注意してください。メソッド本体内のすべてがべき等であることが不可欠です。

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 メソッドのアノテーションが機能する

  1. @EnableTransactionManagement のアノテーションが付けられる構成クラス。

  2. アノテーション付きメソッドを含むサービスオブジェクトには @Service というアノテーションを付ける必要があります。

  3. メソッドの本体はトランザクションで実行されます。

  4. アノテーション付きメソッドを含むサービスオブジェクトは、@Autowired 経由で取得する必要があります。

  5. アノテーション付きメソッドを同じクラスから呼び出しても、トランザクション処理を行うメソッドインターセプターが呼び出されないため、メソッドの呼び出しはサービスとは異なるクラスから行う必要があります。

CouchbaseTransactionalOperator との取引

CouchbaseTransactionalOperator を使用すると、@Transactional を使用するサービスクラスを作成せずに、トランザクションをインラインで構築できます。CouchbaseTransactionalOperator は Bean として利用でき、@Autowired でインスタンス化できます。明示的に作成する場合は、CouchbaseTransactionalOperator.create(manager) (TransactionalOperator.create(manager) ではありません) を使用して作成する必要があります。

例 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 操作は、Spring トランザクションメカニズムを介さずに、transactions().run() のラムダ内で直接動作します。これは、Spring Data Couchbase で Couchbase トランザクションを利用する最も簡単な方法です。

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