Docs Menu
Docs Home
/ /

チュートリアル: 書込み (write) との統合

SPI は、本番環境に対応したアプリケーションの開発を簡素化するオープンソースのJavaフレームワークです。 spring Data MongoDBと組み合わせると、このフレームワークはMongoDBデータベースを操作する効率的な方法を提供します。

このチュートリアルでは、spring Data MongoDBを使用してMongoDBデータベースで操作を実行する、リアクティブな SPIアプリケーションを構築する方法について説明します。このサンプルキャッシュ 残高アプリケーションを使用して、次のアクションを実行する REST API を呼び出すことができます。

  • アカウントを作成または取得する

  • 1 つのアカウントまたは 2 つのアカウント間でトランザクションを実行

spring Data MongoDB はMongoDBのリポジトリをサポートし、データベースを操作するための 2 つのプライマリ方法を提供します。

  • ReactiveMongoRepository: 一般的なデータベース操作へのアクセスを提供する高レベルの抽象化

  • ReactiveMongoTemplate: MongoDB の操作とクエリ構築をより制御できる低レベルの抽象化

MongoDB はマルチドキュメントACIDトランザクション をサポートしており、アトミック性、整合性、分離、耐久性保証を満たす複数の操作を実行できます。 spring Data MongoDB は、 @Transactional アノテーションまたは TransactionalOperatorクラスによるトランザクションの組み込みサポートを提供します。このチュートリアルのサンプルアプリケーションでは、データベース操作のアトミック性を確保するためにトランザクションをサポートするように構成されています。

MongoDBトランザクションの詳細については、 MongoDB Serverマニュアルの「 トランザクション 」を参照してください。

このチュートリアルでは、簡略化されたキャッシュ残高システムを実装する例アプリケーションをダウンロードする方法を説明します。アプリケーションを使用すると、金融アカウントの残高を作成、検索、更新できます。完全なアプリケーションを表示するには、 mdb-spring-boot-reactive GitHubリポジトリ を参照してください。

次のアクションを実行する方法については、チュートリアルを完了してください。

  • 例をダウンロード

  • MongoDBに接続するためのアプリケーションの構成

  • アプリケーション構造と主要なコンポーネントを理解する

  • アプリケーションを実行する

  • 金融アカウントを作成しトランザクションを実行するための POST リクエストの送信

1

このチュートリアルを開始する前に、次のコンポーネントが準備されていることを確認してください。

2

次のコマンドを実行中て、 MongoDB Developer GitHubリポジトリから例アプリケーションをクローンします。

git clone git@github.com:mongodb-developer/mdb-spring-boot-reactive.git

このリポジトリには、spring Data MongoDBを使用してMongoDBデータベースを操作する、リアクティブな SPIアプリケーションが含まれています。

3

例アプリケーションディレクトリ内の src/main/resources/application.propertiesファイルに移動します。このファイルで、次のコードに示すように、spring.data.mongodb.uriプロパティをAtlas 接続 URI に設定します。

spring.data.mongodb.uri=<connection URI>

<connection URI> プレースホルダーを Atlas 接続 URI に置き換えます。

4

ルートディレクトリから次のコマンドを実行して、アプリケーションのスキーマ検証を設定します。

mongosh "<connection URI>" --file setup.js

このコマンドは、金融アカウントの残高が 0 を下回らないようにする制約を作成します。 <connection URI> プレースホルダーを Atlas 接続 URI に置き換えます。

1

AccountController.javaファイルには、アカウントを作成および取得するためのREST APIエンドポイントが含まれています。このファイルは、txn-demo.accountsコレクションにドキュメントを挿入する POST メソッド エンドポイントと、その accountNum 値に基づいてドキュメントを検索する GET メソッド エンドポイントを定義します。次のコードは、これらのメソッドを示しています。

@RestController
public class AccountController {
//...
@PostMapping("/account")
public Mono<Account> createAccount(@RequestBody Account account) {
return accountRepository.save(account);
}
@GetMapping("/account/{accountNum}")
public Mono<Account> getAccount(@PathVariable String accountNum) {
return accountRepository.findByAccountNum(accountNum)
.switchIfEmpty(Mono.error(new AccountNotFoundException()));
}
//...
}

各エンドポイントは、AccountRepository から Mono<Account> 型を返します。これは、基礎となるJava Reactive Streams ドライバーからの抽象化として機能する ReactiveMongoRepository インターフェースです。

2

AccountControllerクラスには、次のトランザクション エンドポイントも含まれます。

  • アカウント残高に追加する .../debit エンドポイント

  • アカウント残高から減算する .../credit エンドポイント

  • あるアカウントから別のアカウントへの転送を実行する .../transfer エンドポイント

これらのエンドポイントには、次の定義があります。

@RestController
public class AccountController {
//...
@PostMapping("/account/{accountNum}/debit")
public Mono<Txn> debitAccount(@PathVariable String accountNum,
@RequestBody Map<String, Object> requestBody) {
//...
txn.addEntry(new TxnEntry(accountNum, amount));
return txnService.saveTransaction(txn).flatMap(txnService::executeTxn);
}
@PostMapping("/account/{accountNum}/credit")
public Mono<Txn> creditAccount(@PathVariable String accountNum,
@RequestBody Map<String, Object> requestBody) {
//...
txn.addEntry(new TxnEntry(accountNum, -amount));
return txnService.saveTransaction(txn).flatMap(txnService::executeTxn);
}
@PostMapping("/account/{from}/transfer")
public Mono<Txn> transfer(@PathVariable String from,
@RequestBody TransferRequest transferRequest) {
//...
txn.addEntry(new TxnEntry(from, -amount));
txn.addEntry(new TxnEntry(to, amount));
return txnService.saveTransaction(txn).flatMap(txnService::executeTxn);
}
//...
}

1 つの TxnEntryオブジェクトは1 つのアカウントに対する変更を表し、Txn は 1 つまたは複数の TxnEntry オブジェクトで構成できます。サンプルコードでは、デビットとクレジットのエンドポイントにより新しい TxnEntryオブジェクトが1 つ作成され、転送エンドポイントにより 2 つの TxnEntry オブジェクトが作成されます。

3

AccountRepository インターフェースは ReactiveMongoRepository を拡張し、次のクエリメソッドを定義します。

public interface AccountRepository extends ReactiveMongoRepository<Account, String> {
@Query("{accountNum:'?0'}")
Mono<Account> findByAccountNum(String accountNum);
@Update("{'$inc':{'balance': ?1}}")
Mono<Long> findAndIncrementBalanceByAccountNum(String accountNum, double increment);
}

コードは次の注釈を使用し、メソッド引数で動的に置き換えられるMongoDB をクエリするときにプレースホルダーを使用できます。

  • @Query: findByAccountNum() メソッドに注釈を付け、ドキュメントを検索するためのクエリ条件を指定します。 ?0 プレースホルダーは accountNum パラメータ値で置き換えられます。

  • @Update: findAndIncrementBalanceByAccountNum() メソッドに注釈を付け、一致するドキュメントに対して実行するアップデート操作を指定します。 MongoDB の $inc 演算子を使用してバランスを増やすために、?1 プレースホルダーは increment パラメーター値に置き換えられます。

注意

前の手順で説明した POST エンドポイントではaccountRepository.save() メソッドが使用されていますが、このメソッドを定義する必要はありません。save() メソッドおよびその他の多くの基本メソッドは、 の継承チェーン内のインターフェースによってすでに宣言されています。ReactiveMongoRepository

4

TxnServiceクラスはトランザクションの実行を取り扱います。このクラスには、次のコードが含まれています。

@Service
public class TxnService {
//...
public Mono<Txn> saveTransaction(Txn txn) {
return txnTemplate.save(txn);
}
public Mono<Txn> executeTxn(Txn txn) {
return updateBalances(txn)
.onErrorResume(DataIntegrityViolationException.class
/*lambda expression to handle error*/)
.onErrorResume(AccountNotFoundException.class
/*lambda expression to handle error*/)
.then(txnTemplate.findAndUpdateStatusById(txn.getId(), TxnStatus.SUCCESS));
}
public Flux<Long> updateBalances(Txn txn) {
Flux<Long> updatedCounts = Flux.fromIterable(txn.getEntries()).concatMap(
entry -> accountRepository.findAndIncrementBalanceByAccountNum(
entry.getAccountNum(), entry.getAmount())
);
return updatedCounts.handle(/*...*/);
}
}

TxnServiceクラスには次のメソッドが含まれています。

  • saveTransaction(): Txnドキュメントを transactionsコレクションに保存

  • executeTxn(): updateBalances() メソッドを呼び出し、Txnドキュメントでトランザクション ステータスをアップデートします

  • updateBalances(): 各 TxnEntry を反復処理し、各 accountドキュメントに対応する更新を行います

5

前の手順で説明した saveTransaction() メソッドと executeTxn() メソッドはどちらも、TxnTemplateクラスで定義されたメソッドを使用します。これらのメソッドには、次の定義があります。

@Service
public class TxnTemplate {
//...
public Mono<Txn> save(Txn txn) {
return template.save(txn);
}
public Mono<Txn> findAndUpdateStatusById(String id, TxnStatus status) {
Query query = query(where("_id").is(id));
Update update = update("status", status);
FindAndModifyOptions options = FindAndModifyOptions.options().returnNew(true);
return template.findAndModify(query, update, options, Txn.class);
}
//...
}

これらのメソッドは ReactiveMongoTemplate を使用してMongoDBとやりとりします。

6

アカウント間で送金を転送する場合、マルチドキュメントトランザクションを使用できます。 2 つのアカウントにわたる更新は不可分である必要があり、トランザクションはデータの不可分性を確保します。

spring のトランザクション サポートにアクセスするために、サンプルアプリケーションはReactiveMongoTransactionManager bean を ReactiveMongoConfig.javaファイルに追加します。

@Configuration
public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration {
//...
@Bean
ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) {
return new ReactiveMongoTransactionManager(dbFactory);
}
}

TransactionalOperatorオブジェクトまたは @Transactional アノテーションを使用して、トランザクションのスコープを定義できます。 TxnServiceクラスは両方のアプローチを示しています。

Tip

spring Data Transactions

spring Data MongoDBのトランザクションとセッションの詳細については、spring Data ドキュメントの「 セッションとトランザクション 」を参照してください。

1

アプリケーションを起動するには、プロジェクトの ルートディレクトリから次のコマンドを実行します。

mvn spring-boot:run

アプリケーションの起動後は、http://localhost:8080 でアクセスできます。その後、 REST APIエンドポイントを使用してアカウントを作成し、トランザクションを実行できます。

2

アカウント番号が 12345 である新しい金融アカウントを作成するには、 シェルで次のコマンドを実行します。

curl --location 'localhost:8080/account' \
--header 'Content-Type: application/json' \
--data '{
"accountNum": "12345"
}'

このコマンドは、/account エンドポイントに POSTリクエストを送信し、accountsコレクションに新しい金融アカウントドキュメントを作成します。

3

前のステップで作成されたアカウントにデプロイするには、 シェルで次のコマンドを実行します。

curl --location 'localhost:8080/account/12345/debit' \
--header 'Content-Type: application/json' \
--data '{
"amount": 1000
}'

このコマンドは、対応する accountドキュメントの amount の値を 1000 ずつ増やすためのリクエストを .../debit エンドポイントに送信します。

4

あるアカウントから別のアカウントに送金するには、アカウント番号が 67890 の新しいアカウントを作成します。

curl --location 'localhost:8080/account' \
--header 'Content-Type: application/json' \
--data '{
"accountNum": "67890"
}'

次に、次のコマンドを実行中て、500 ドルをこの新しいアカウントに転送します。

curl --location 'localhost:8080/account/12345/transfer' \
--header 'Content-Type: application/json' \
--data '{
"to": "67890",
"amount": 500
}'

このコマンドは、転送残高を完了するためのリクエストを .../transfer エンドポイントに送信します。

spring Data MongoDBの詳細については、spring Data MongoDBの参照ドキュメントを参照してください。

完全な例アプリケーションを表示するには、 mdb-spring-boot-reactive GitHubリポジトリ を参照してください。

戻る

使用中の暗号化

項目一覧