Docs Menu
Docs Home
/ / /
Java 同期
/ /

複合演算子

項目一覧

  • Overview
  • 複合演算子の使用方法
  • 検索と更新
  • 検索と置換
  • 検索と削除
  • 競合状態の回避
  • 競合状態の例
  • 競合条件がない例

このガイドでは、MongoDB Java ドライバーを使用して複合操作を実行する方法を学習します。

複合操作 は、1 つのアトミック操作として実行される読み取り操作と書込み操作で構成されます。 アトミック操作とは、完全に完了するか、まったく完了しない操作です。 アトミック操作は部分的には完了できません。

アトミック操作は、コード内の競合状態を回避するのに役立ちます。 コードの動作が制御できないイベントの順序に依存している場合、競合状態が発生します。

MongoDB は次の複合操作をサポートしています。

  • 1 つのドキュメントを検索して更新

  • 1 つのドキュメントを検索して置換

  • 1 つのドキュメントを検索して削除

複数のドキュメントの読み取りと書き込みなど、より複雑なタスクをアトミックに実行する必要がある場合は、トランザクションを使用します。 トランザクションは MongoDB やその他のデータベースの機能であり、データベースコマンドの任意のシーケンスをアトミック操作として定義できます。

アトミック操作とアトミック性の詳細については、MongoDB マニュアルの「 アトミシティとトランザクション 」を参照してください。

トランザクションの詳細については、 トランザクション に関する MongoDB マニュアルのエントリを参照してください。

このセクションでは、MongoDB Java ドライバーで各複合操作を使用する方法を示します。

次の例では、これら 2 つのサンプル ドキュメントを含むコレクションを使用します。

{"_id": 1, "food": "donut", "color": "green"}
{"_id": 2, "food": "pear", "color": "yellow"}

次の例の完全なコードは Github ここで利用できます。

注意

書き込みの前、後は?

デフォルトでは、各複合操作は、書込み操作の前の状態で見つかったドキュメントを返します。 複合操作に対応するオプション クラスを使用すると、書込み (write) 操作後に 状態で見つかったドキュメントを検索できます。 この構成の例については、以下の「 検索と置換 の例」を参照してください。

1 つのドキュメントを検索して更新するには、 MongoCollectionクラスの findOneAndUpdate()メソッドを使用します。 findOneAndUpdate()nullメソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。

次の例では、 findOneAndUpdate()メソッドを使用して、 colorフィールドが"green"に設定されているドキュメントを検索し、そのドキュメントのfoodフィールドを"pizza"に更新します。

この例では、 FindOneAndUpdateOptionsインスタンスを使用して次のオプションも指定しています。

  • プロジェクションを使用して、見つかったドキュメントから_idフィールドを除外します。

  • クエリに一致するドキュメントがない場合に、クエリフィルターで指定されたドキュメントを挿入する アップサート を指定します。

  • MongoDB インスタンスで、この操作の最大実行時間を 5 秒に設定します。 操作に時間がかかる場合、 findOneAndUpdate()メソッドはMongoExecutionTimeoutExceptionをスローします。

// <MongoCollection set up code here>
// Creates a projection to exclude the "_id" field from the retrieved documents
Bson projection = Projections.excludeId();
// Creates a filter to match documents with a "color" value of "green"
Bson filter = Filters.eq("color", "green");
// Creates an update document to set the value of "food" to "pizza"
Bson update = Updates.set("food", "pizza");
// Defines options that specify projected fields, permit an upsert and limit execution time
FindOneAndUpdateOptions options = new FindOneAndUpdateOptions().
projection(projection).
upsert(true).
maxTime(5, TimeUnit.SECONDS);
// Updates the first matching document with the content of the update document, applying the specified options
Document result = collection.findOneAndUpdate(filter, update, options);
// Prints the matched document in its state before the operation
System.out.println(result.toJson());

上記のコードの出力は次のようになります。

{"food": "pizza", "color": "green"}

Projectionsクラスの詳細については、 プロジェクション ビルダに関するガイドを参照してください。

アップサート操作の詳細については、 アップサート に関するガイドをご覧ください。

このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。

1 つのドキュメントを検索して置き換えるには、 MongoCollectionクラスのfindOneAndReplace()メソッドを使用します。 findOneAndReplace()nullメソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。

次の例では、 findOneAndReplace()メソッドを使用して、 colorフィールドが"green"に設定されているドキュメントを検索し、それを次のドキュメントに置き換えます。

{"music": "classical", "color": "green"}

この例ではまた、 FindOneAndReplaceOptionsインスタンスを使用して、置換操作後に返されるドキュメントが の状態であることを指定しています。

// <MongoCollection set up code here>
// Creates instructions to replace the matching document with a new document
Bson filter = Filters.eq("color", "green");
Document replace = new Document("music", "classical").append("color", "green");
// Defines options specifying that the operation should return a document in its post-operation state
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().
returnDocument(ReturnDocument.AFTER);
// Atomically finds and replaces the matching document and prints the replacement document
Document result = collection.findOneAndReplace(filter, replace, options);
System.out.println(result.toJson());

上記のコードの出力は次のようになります。

{"_id": 1, "music": "classical", "color": "green"}

このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。

1 つのドキュメントを検索して削除するには、 MongoCollectionクラスのfindOneAndDelete()メソッドを使用します。 findOneAndDelete()nullメソッドは見つかったドキュメントを返します。クエリに一致するドキュメントがない場合は メソッドは返します。

次の例では、 findOneAndDelete()メソッドを使用して、 _idフィールドの 値が最も大きいドキュメントを検索して削除します。

この例では、 FindOneAndDeleteOptionsインスタンスを使用して、 _idフィールドで降順の並べ替えを指定しています。

// <MongoCollection set up code here>
Bson sort = Sorts.descending("_id");
// Creates an empty filter to match all documents in the collection
Bson filter = Filters.empty();
// Defines options that specify a descending sort on the "_id" field
FindOneAndDeleteOptions options = new FindOneAndDeleteOptions().
sort(sort);
// Deletes the document containing the highest "_id" value and prints the deleted document
Document result = collection.findOneAndDelete(filter, options);
System.out.println(result.toJson());

上記のコードの出力は次のようになります。

{"_id": 2, "food": "pear", "color": "yellow"}

Sortsクラスの詳細については、 ソート ビルダに関するガイド を参照してください。

このセクションで説明されるメソッドとクラスの詳細については、次の API ドキュメントを参照してください。

このセクションでは、2 つの例について説明します。 最初の例には競合状態が含まれており、2 番目の例では 複合操作 を使用して最初の例に存在する競合状態を回避しています。

両方の例では、1 つの部屋を持つプロパティを実行し、この部屋をゲスト チェックアウトするのに役立つ小さな Java プログラムがあるとします。

MongoDB 内の次のドキュメントは、この部屋を表しています。

{"_id": 1, "guest": null, "room": "Blue Room", "reserved": false}

この例の完全なコードは Github で入手できます。

アプリがこのbookARoomメソッドを使用して、部屋をゲスト用にチェックアウトするとします。

public void bookARoom() {
// Creates a filter to match documents representing available rooms
Bson filter = Filters.eq("reserved", false);
// Retrieves a document that represents the first available room
Document myRoom = this.collection.find(filter).first();
// Prints a message if no documents representing available rooms are found
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
String myRoomName = myRoom.getString("room");
// Prints a message that guest that successfully booked the room
System.out.println("You got the " + myRoomName + " " + this.guest);
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter that matches the "_id" field of the first available room
Bson roomFilter = Filters.eq("_id", myRoom.get("_id", Integer.class));
// Updates the first matching document to mark it as reserved
this.collection.updateOne(roomFilter, update);
}

Jan と Pat の 2 人の別々の利用者が、この方法で同時に部屋を予約しようとしているとします。

Jan には次の出力が表示されます。

You got the Blue Room Jan

そして、Pat は次の出力を確認します。

You got the Blue Room Pat

データベースを確認すると、次のことが表示されます。

{"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": true}

パッチは Atlas がお客様のインスタンスに表示されると、Jan は自分の部屋を占有することになります。 何が起こるでしたか。

MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。

  • 1 月の空の間を検索して返す

  • パッチの空のテーブルを検索して返します

  • パッチに予約されている部屋を更新する

  • 1 月に予約されている部屋を更新する

一時的に、Pat がこの部屋を予約していましたが、Jan の更新操作が最後に実行されたため、ドキュメントはゲストとして"Jan"を実行していることに注意してください。

複合操作 を使用して競合状態を回避し、常にユーザーに正しいメッセージを提供できるようにしましょう。

public void bookARoom(){
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter to match a document representing an available room
Bson filter = Filters.eq("reserved", false);
// Updates the first document that matches the filter to mark it as reserved
Document myRoom = this.collection.findOneAndUpdate(filter, update);
// Prints a message when there are no available rooms
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
// Prints the name of the guest that successfully booked the room
String myRoomName = myRoom.getString("room");
System.out.println("You got the " + myRoomName + " " + this.guest);
}

Jan と Pat の 2 人の別々の利用者が、この方法で同時に部屋を予約しようとしているとします。

Jan には次の出力が表示されます。

You got the Blue Room Jan

そして、Pat は次の出力を確認します。

Sorry, we are booked Pat

データベースを確認すると、次のことが表示されます。

{"_id":1, "guest":"Jan", "room":"Blue Room", "reserved":true}

Pat は正しいメッセージを取得しました。 予約が取得されなかったため、

MongoDB インスタンスの観点から発生したイベントの順序は以下のとおりです。

  • 1 月 日の空の間を見つけて予約します。

  • パターン 用の空のスペースを見つけて予約してください。 残りのルームがないため、 nullが返されます。

重要

書込みロック (write lock)

MongoDB インスタンスは、複合操作の実行中、変更しているドキュメントに対して書込みロック (write lock) を配置します。

Updatesクラスの詳細については、 アップデート ビルダに関するガイド をご覧ください。

Filtersクラスの詳細については、 フィルター ビルダに関するガイド をご覧ください。

findOneAndUpdate()メソッドの詳細については、 MongoCollection クラスの API ドキュメントを参照してください。

戻る

クエリを指定する