ドキュメントの保存、更新、削除

MongoTemplate / ReactiveMongoTemplatge を使用すると、ドメインオブジェクトを保存、更新、削除し、それらのオブジェクトを MongoDB に保存されているドキュメントにマップできます。命令型 API とリアクティブ API の API シグネチャーは、戻り値の型が異なるだけで、ほとんど同じです。同期 API は void、単一 Object および List を使用しますが、リアクティブ API は Mono<Void>Mono<Object>Flux で構成されます。

次のクラスを検討してください。

public class Person {

	private String id;
	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}

前の例の Person クラスを考慮すると、次の例に示すように、オブジェクトを保存、更新、削除できます。

  • 命令的

  • リアクティブ

public class MongoApplication {

  private static final Log log = LogFactory.getLog(MongoApplication.class);

  public static void main(String[] args) {

    MongoOperations template = new MongoTemplate(new SimpleMongoClientDbFactory(MongoClients.create(), "database"));

    Person p = new Person("Joe", 34);

    // Insert is used to initially store the object into the database.
    template.insert(p);
    log.info("Insert: " + p);

    // Find
    p = template.findById(p.getId(), Person.class);
    log.info("Found: " + p);

    // Update
    template.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class);
    p = template.findOne(query(where("name").is("Joe")), Person.class);
    log.info("Updated: " + p);

    // Delete
    template.remove(p);

    // Check that deletion worked
    List<Person> people =  template.findAll(Person.class);
    log.info("Number of people = : " + people.size());


    template.dropCollection(Person.class);
  }
}

前述の例では、次のログ出力が生成されます ( MongoTemplate からのデバッグメッセージを含む)。

DEBUG apping.MongoPersistentEntityIndexCreator:  80 - Analyzing class class org.spring.example.Person for index information.
DEBUG work.data.mongodb.core.MongoTemplate: 632 - insert Document containing fields: [_class, age, name] in collection: person
INFO               org.spring.example.MongoApp:  30 - Insert: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "_id" : { "$oid" : "4ddc6e784ce5b1eba3ceaf5c"}} in db.collection: database.person
INFO               org.spring.example.MongoApp:  34 - Found: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate: 778 - calling update using query: { "name" : "Joe"} and update: { "$set" : { "age" : 35}} in collection: person
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "name" : "Joe"} in db.collection: database.person
INFO               org.spring.example.MongoApp:  39 - Updated: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=35]
DEBUG work.data.mongodb.core.MongoTemplate: 823 - remove using query: { "id" : "4ddc6e784ce5b1eba3ceaf5c"} in collection: person
INFO               org.spring.example.MongoApp:  46 - Number of people = : 0
DEBUG work.data.mongodb.core.MongoTemplate: 376 - Dropped collection [database.person]
public class ReactiveMongoApplication {

  private static final Logger log = LoggerFactory.getLogger(ReactiveMongoApplication.class);

  public static void main(String[] args) throws Exception {

    CountDownLatch latch = new CountDownLatch(1);

    ReactiveMongoTemplate template = new ReactiveMongoTemplate(MongoClients.create(), "database");

    template.insert(new Person("Joe", 34)).doOnNext(person -> log.info("Insert: " + person))
      .flatMap(person -> template.findById(person.getId(), Person.class))
      .doOnNext(person -> log.info("Found: " + person))
      .zipWith(person -> template.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class))
      .flatMap(tuple -> template.remove(tuple.getT1())).flatMap(deleteResult -> template.findAll(Person.class))
      .count().doOnSuccess(count -> {
        log.info("Number of people: " + count);
        latch.countDown();
      })

      .subscribe();

    latch.await();
  }
}

MongoConverter は、Id プロパティ名を (規則に従って) 認識することにより、データベースに格納されている String と ObjectId の間で暗黙的な変換を引き起こしました。

前述の例は、MongoTemplate / ReactiveMongoTemplate での保存、更新、削除操作の使用方法を示すことを目的としており、複雑なマッピング機能を示すことを目的としたものではありません。前の例で使用されているクエリ構文については、"ドキュメントのクエリ" セクションで詳しく説明します。

MongoDB では、すべてのドキュメントに _id フィールドが必要です。このフィールドの特殊な処理の詳細については、ID の取り扱いセクションを参照してください。
MongoDB コレクションには、さまざまな型のインスタンスを表すドキュメントを含めることができます。詳細については、型 マッピングを参照してください。

挿入 / 保存

MongoTemplate には、オブジェクトを保存および挿入するための便利なメソッドがいくつかあります。変換プロセスをよりきめ細かく制御するには、Spring コンバーターを MappingMongoConverter に登録できます (例: Converter<Person, Document> および Converter<Document, Person>)。

挿入操作と保存操作の違いは、オブジェクトがまだ存在しない場合、保存操作では挿入が実行されることです。

保存操作を使用する簡単な例は、POJO を保存することです。この場合、コレクション名はクラスの名前 (完全修飾名ではありません) によって決まります。特定のコレクション名を使用して保存操作を呼び出すこともできます。マッピングメタデータを使用して、オブジェクトを保存するコレクションをオーバーライドできます。

挿入または保存するときに、Id プロパティが設定されていない場合、その値はデータベースによって自動生成されると想定されます。ObjectId の自動生成が成功するには、クラス内の Id プロパティまたはフィールドの型が StringObjectId、または BigInteger である必要があります。

次の例は、ドキュメントを保存し、その内容を取得する方法を示しています。

MongoTemplate を使用したドキュメントの挿入と取り出し
  • 命令的

  • リアクティブ

import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Criteria.query;

//...

template.insert(new Person("Bob", 33));

Person person = template.query(Person.class)
    .matching(query(where("age").is(33)))
    .oneValue();
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Criteria.query;

//...

Mono<Person> person = mongoTemplate.insert(new Person("Bob", 33))
    .then(mongoTemplate.query(Person.class)
        .matching(query(where("age").is(33)))
        .one());

次の挿入および保存操作が利用可能です。

  • void save (Object objectToSave): オブジェクトをデフォルトのコレクションに保存します。

  • void save (Object objectToSave, String collectionName): オブジェクトを指定されたコレクションに保存します。

同様の挿入操作のセットも利用できます。

  • void insert (Object objectToSave): オブジェクトをデフォルトのコレクションに挿入します。

  • void insert (Object objectToSave, String collectionName): 指定されたコレクションにオブジェクトを挿入します。

_id フィールドがマッピング層でどのように処理されるか

MongoDB では、すべてのドキュメントに _id フィールドが必要です。これを指定しない場合、サーバーは識別子の種類を認識しないため、ドライバーはドメインモデルを考慮せずに生成された値を ObjectId に割り当てます。MappingMongoConverter を使用する場合、Java クラスのプロパティがこの _id フィールドにマップされる方法は、特定のルールによって決まります。

  1. @Id (org.springframework.data.annotation.Id) のアノテーションが付けられたプロパティまたはフィールドは、_id フィールドにマップされます。

  2. アノテーションのない id という名前のプロパティまたはフィールドは、_id フィールドにマップされます。

以下に、MappingMongoConverter ( MongoTemplate のデフォルト) を使用する場合に、_id ドキュメントフィールドにマップされたプロパティでどのような型変換が行われるか (存在する場合) を概説します。

  1. 可能であれば、Java クラスで String として宣言された id プロパティまたはフィールドは、Spring Converter<String, ObjectId> を使用して ObjectId に変換され、格納されます。有効な変換ルールは MongoDB Java ドライバーに委譲されます。ObjectId に変換できない場合、値は文字列としてデータベースに保存されます。

  2. Java クラスで BigInteger として宣言された id プロパティまたはフィールドは、Spring Converter<BigInteger, ObjectId> を使用して ObjectId に変換され、格納されます。

前述のルールセットで指定されたフィールドまたはプロパティが Java クラスに存在しない場合、暗黙的な _id ファイルがドライバーによって生成されますが、Java クラスのプロパティまたはフィールドにはマップされません。

クエリと更新の際、MongoTemplate はドキュメントを保存するための前述のルールに対応するコンバーターを使用して、クエリで使用されるフィールド名と型がドメインクラスの内容と一致できるようにします。

一部の環境では、Spring Data マッピングレイヤーを介して実行されなかった MongoDB に格納されたデータなど、Id 値をマッピングするためのカスタマイズされたアプローチが必要です。ドキュメントには、ObjectId または String として表現できる _id 値を含めることができます。ストアからドメイン型にドキュメントを読み戻すことは問題なく機能します。id を介したドキュメントのクエリは、暗黙的な ObjectId 変換により面倒になる場合があります。このメソッドではドキュメントを取得できません。このような場合、@MongoId は実際の ID マッピングの試行をより詳細に制御できます。

例 1: @MongoId マッピング
public class PlainStringId {
  @MongoId String id; (1)
}

public class PlainObjectId {
  @MongoId ObjectId id; (2)
}

public class StringToObjectId {
  @MongoId(FieldType.OBJECT_ID) String id; (3)
}
1ID はそれ以上変換されずに String として扱われます。
2ID は ObjectId として扱われます。
3 指定された String が有効な ObjectId 16 進数である場合、ID は ObjectId として扱われ、それ以外の場合は String として扱われます。@Id 用途に対応します。

ドキュメントはどのコレクションに保存されますか ?

ドキュメントに使用されるコレクション名を管理するには 2 つの方法があります。使用されるデフォルトのコレクション名は、小文字で始まるように変更されたクラス名です。com.test.Person クラスは person コレクションに格納されます。@Document アノテーションを使用して別のコレクション名を指定することで、これをカスタマイズできます。選択した MongoTemplate メソッド呼び出しの最後のパラメーターとして独自のコレクション名を指定することで、コレクション名をオーバーライドすることもできます。

個々のオブジェクトの挿入または保存

MongoDB ドライバーは、1 回の操作でドキュメントのコレクションを挿入することをサポートしています。MongoOperations インターフェースの次のメソッドは、この機能をサポートしています。

  • 入れる : オブジェクトを挿入します。同じ id を持つ既存のドキュメントが存在する場合、エラーが生成されます。

  • insertAll: 最初のパラメーターとしてオブジェクトの Collection を取ります。このメソッドは各オブジェクトをインスペクションし、前に指定したルールに基づいて適切なコレクションに挿入します。

  • 保存 : オブジェクトを保存し、同じ id を持つ可能性のあるオブジェクトを上書きします。

複数のオブジェクトをバッチに挿入する

MongoDB ドライバーは、1 回の操作でドキュメントのコレクションを挿入することをサポートしています。MongoOperations インターフェースの次のメソッドは、insert または専用の BulkOperations インターフェースを介してこの機能をサポートします。

バッチ挿入
  • 命令的

  • リアクティブ

Collection<Person> inserted = template.insert(List.of(...), Person.class);
Flux<Person> inserted = template.insert(List.of(...), Person.class);
一括挿入
  • 命令的

  • リアクティブ

BulkWriteResult result = template.bulkOps(BulkMode.ORDERED, Person.class)
    .insert(List.of(...))
    .execute();
Mono<BulkWriteResult> result = template.bulkOps(BulkMode.ORDERED, Person.class)
    .insert(List.of(...))
    .execute();

バッチとバルクのサーバーパフォーマンスは同じです。ただし、一括操作ではライフサイクルイベントが公開されません。

insert を呼び出す前に設定されていない @Version プロパティは、1 ( int のような単純型の場合) またはラッパー型 (例: Integer) の 0 で自動的に初期化されます。
詳細については、"楽観的ロック" セクションを参照してください。

更新

更新の場合は、MongoOperation.updateFirst を使用して最初に見つかったドキュメントを更新することも、MongoOperation.updateMulti メソッドまたは Fluent API の all を使用して、クエリに一致することが見つかったすべてのドキュメントを更新することもできます。次の例は、$inc 演算子を使用して残高に 1 回限りの $50.00 ボーナスを追加するすべての SAVINGS アカウントの更新を示しています。

MongoTemplate / ReactiveMongoTemplate を使用したドキュメントの更新
  • 命令的

  • リアクティブ

import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.springframework.data.mongodb.core.query.Update;

// ...

UpdateResult result = template.update(Account.class)
    .matching(where("accounts.accountType").is(Type.SAVINGS))
    .apply(new Update().inc("accounts.$.balance", 50.00))
    .all();
import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.springframework.data.mongodb.core.query.Update;

// ...

Mono<UpdateResult> result = template.update(Account.class)
    .matching(where("accounts.accountType").is(Type.SAVINGS))
    .apply(new Update().inc("accounts.$.balance", 50.00))
    .all();

前に説明した Query に加えて、Update オブジェクトを使用して更新定義を提供します。Update クラスには、MongoDB で使用できる更新修飾子に一致するメソッドがあります。ほとんどのメソッドは Update オブジェクトを返し、API に流れるようなスタイルを提供します。

@Version プロパティが Update に含まれていない場合は、自動的に増分されます。詳細については、"楽観的ロック" セクションを参照してください。

ドキュメントの更新を実行するためのメソッド

  • updateFirst: クエリドキュメント条件に一致する最初のドキュメントを更新されたドキュメントで更新します。

  • updateMulti: クエリドキュメント条件に一致するすべてのオブジェクトを更新されたドキュメントで更新します。

updateFirst は順序付けをサポートしていません。Sort を適用するには findAndModify を使用してください。
更新操作のインデックスヒントは、Query.withHint(…​) 経由で提供できます。

Update クラスのメソッド

Update クラスのメソッドは連鎖することを目的としているため、Update クラスで少しの「構文糖」を使用できます。また、public static Update update(String key, Object value) と静的インポートを使用して、新しい Update インスタンスの作成を開始することもできます。

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

  • Update addToSet (String key, Object value)  $addToSet 更新修飾子を使用した更新

  • Update currentDate (String key)  $currentDate 更新修飾子を使用した更新

  • Update currentTimestamp (String key)  $typetimestamp を使用した $currentDate 更新修飾子を使用した更新

  • Update inc (String key, Number inc)  $inc 更新修飾子を使用した更新

  • Update  最大 (String key, Object max)  $max 更新修飾子を使用した更新

  • Update  最小 $min 更新修飾子を使用した (String key, Object min) 更新

  • Update  $mul 更新修飾子を使用した (String key, Number multiplier) 更新の乗算

  • Update  ポップ (String key, Update.Position pos)  $pop 更新修飾子を使用した更新

  • Update  $pull 更新修飾子を使用した (String key, Object value) 更新のプル

  • Update pullAll (String key, Object[] values)  $pullAll 更新修飾子を使用した更新

  • Update  プッシュ $push 更新修飾子を使用した (String key, Object value) 更新

  • Update pushAll (String key, Object[] values)  $pushAll 更新修飾子を使用した更新

  • Update  $rename 更新修飾子を使用した (String oldName, String newName) Update の名前変更

  • Update  セット $set 更新修飾子を使用した (String key, Object value) 更新

  • Update setOnInsert (String key, Object value)  $setOnInsert 更新修飾子を使用した更新

  • Update  設定解除 (String key)  $unset 更新修飾子を使用した更新

$push や $addToSet などの一部の更新修飾子では、追加の演算子のネストが可能です。

// { $push : { "category" : { "$each" : [ "spring" , "data" ] } } }
new Update().push("category").each("spring", "data")

// { $push : { "key" : { "$position" : 0 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));

// { $push : { "key" : { "$slice" : 5 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel"));

// { $addToSet : { "values" : { "$each" : [ "spring" , "data" , "mongodb" ] } } }
new Update().addToSet("values").each("spring", "data", "mongodb");

集約パイプラインの更新

MongoOperations および ReactiveMongoOperations によって公開される更新メソッドは、AggregationUpdate 経由で集約パイプラインも受け入れます。AggregationUpdate を使用すると、更新操作で MongoDB 4.2 アグリゲーション (英語) を利用できるようになります。更新で集計を使用すると、1 回の操作で複数のステージと複数の条件を表現することにより、1 つ以上のフィールドを更新できます。

更新は次の段階で構成されます。

  • AggregationUpdate.set(…​).toValue(…​) → $set : { …​ }

  • AggregationUpdate.unset(…​) → $unset : [ …​ ]

  • AggregationUpdate.replaceWith(…​) → $replaceWith : { …​ }

例 2: 集計の更新
AggregationUpdate update = Aggregation.newUpdate()
    .set("average").toValue(ArithmeticOperators.valueOf("tests").avg())     (1)
    .set("grade").toValue(ConditionalOperators.switchCases(                 (2)
        when(valueOf("average").greaterThanEqualToValue(90)).then("A"),
        when(valueOf("average").greaterThanEqualToValue(80)).then("B"),
        when(valueOf("average").greaterThanEqualToValue(70)).then("C"),
        when(valueOf("average").greaterThanEqualToValue(60)).then("D"))
        .defaultTo("F")
    );

template.update(Student.class)                                              (3)
    .apply(update)
    .all();                                                                 (4)
db.students.update(                                                         (3)
   { },
   [
     { $set: { average : { $avg: "$tests" } } },                            (1)
     { $set: { grade: { $switch: {                                          (2)
                           branches: [
                               { case: { $gte: [ "$average", 90 ] }, then: "A" },
                               { case: { $gte: [ "$average", 80 ] }, then: "B" },
                               { case: { $gte: [ "$average", 70 ] }, then: "C" },
                               { case: { $gte: [ "$average", 60 ] }, then: "D" }
                           ],
                           default: "F"
     } } } }
   ],
   { multi: true }                                                          (4)
)
1 最初の $set ステージでは、テストフィールドの平均に基づいて新しいフィールドの平均を計算します。
22 番目の $set ステージは、最初の集計ステージで計算された平均フィールドに基づいて、新しいフィールドグレードを計算します。
3 パイプラインは学生コレクション上で実行され、集計フィールドマッピングに Student を使用します。
4 コレクション内の一致するすべてのドキュメントに更新を適用します。

アップサート

updateFirst 操作の実行に関連して、クエリに一致するドキュメントが見つからない場合に挿入を実行する upsert 操作を実行することもできます。挿入されるドキュメントは、クエリドキュメントと更新ドキュメントの組み合わせです。次の例は、upsert メソッドの使用方法を示しています。

  • 命令的

  • リアクティブ

UpdateResult result = template.update(Person.class)
  .matching(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update"))
  .apply(update("address", addr))
  .upsert();
Mono<UpdateResult> result = template.update(Person.class)
  .matching(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update"))
  .apply(update("address", addr))
  .upsert();
upsert は順序付けをサポートしていません。Sort を適用するには findAndModify を使用してください。

@Version プロパティが Update に含まれていない場合は、自動的に初期化されます。詳細については、"楽観的ロック" セクションを参照してください。

コレクション内のドキュメントの置換

MongoTemplate 経由で利用できるさまざまな replace メソッドを使用すると、最初に一致したドキュメントをオーバーライドできます。一致するものが見つからない場合は、ReplaceOptions に適切な構成を提供することで、(前のセクションで概説したように) 新しいものを更新 / 挿入できます。

1 つを交換する
Person tom = template.insert(new Person("Motte", 21)); (1)
Query query = Query.query(Criteria.where("firstName").is(tom.getFirstName())); (2)
tom.setFirstname("Tom"); (3)
template.replace(query, tom, ReplaceOptions.none()); (4)
1 新しいドキュメントを挿入します。
2 置換する単一のドキュメントを識別するために使用されるクエリ。
3 既存と同じ _id を保持するか、_id をまったく保持しない置換ドキュメントを設定します。
4 置換操作を実行します。1 つを Upsert に置き換えます
Person tom = new Person("id-123", "Tom", 21) (1)
Query query = Query.query(Criteria.where("firstName").is(tom.getFirstName()));
template.replace(query, tom, ReplaceOptions.replaceOptions().upsert()); (2)
1 更新 / 挿入には _id 値が存在する必要があります。そうでない場合、MongoDB は、ドメイン型に互換性のない ObjectId を使用して新しい値を作成する可能性があります。MongoDB はドメイン型を認識しないため、@Field(targetType) ヒントは考慮されず、結果の ObjectId はドメインモデルと互換性がない可能性があります。
2 一致するものが見つからない場合は、upsert を使用して新しいドキュメントを挿入します

置換操作で既存のドキュメントの _id を変更することはできません。upsert では、MongoDB はエントリの新しい ID を決定する 2 つのメソッドを使用します。* _id は {"_id" : 1234 } と同様にクエリ内で使用されます。* _id は置換ドキュメント内に存在します。どちらのメソッドでも _id が指定されていない場合、MongoDB はドキュメント用に新しい ObjectId を作成します。これにより、使用されているドメイン型 id プロパティの型が異なる場合、マッピングやデータ検索の誤動作が発生する可能性があります。Long

検索と変更

MongoCollection の findAndModify(…) メソッドは、ドキュメントを更新し、1 回の操作で古いドキュメントまたは新しく更新されたドキュメントを返すことができます。MongoTemplate は、Query および Update クラスを受け取り、Document から POJO に変換する 4 つの findAndModify オーバーロードメソッドを提供します。

<T> T findAndModify(Query query, Update update, Class<T> entityClass);

<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);

<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);

<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);

次の例では、いくつかの Person オブジェクトをコンテナーに挿入し、findAndUpdate 操作を実行します。

template.insert(new Person("Tom", 21));
template.insert(new Person("Dick", 22));
template.insert(new Person("Harry", 23));

Query query = new Query(Criteria.where("firstName").is("Harry"));
Update update = new Update().inc("age", 1);

Person oldValue = template.update(Person.class)
  .matching(query)
  .apply(update)
  .findAndModifyValue(); // oldValue.age == 23

Person newValue = template.query(Person.class)
  .matching(query)
  .findOneValue(); // newValye.age == 24

Person newestValue = template.update(Person.class)
  .matching(query)
  .apply(update)
  .withOptions(FindAndModifyOptions.options().returnNew(true)) // Now return the newly updated document when updating
  .findAndModifyValue(); // newestValue.age == 25

FindAndModifyOptions メソッドを使用すると、returnNewupsertremove のオプションを設定できます。前のコードスニペットを継承した例は次のとおりです。

Person upserted = template.update(Person.class)
  .matching(new Query(Criteria.where("firstName").is("Mary")))
  .apply(update)
  .withOptions(FindAndModifyOptions.options().upsert(true).returnNew(true))
  .findAndModifyValue()

@Version プロパティが Update に含まれていない場合は、自動的に増分されます。詳細については、"楽観的ロック" セクションを参照してください。

検索と置換

Document 全体を置き換える最も簡単な方法は、save メソッドを使用して id を使用することです。ただし、これは常に実現可能であるとは限りません。findAndReplace は、単純なクエリを通じて置換するドキュメントを特定できる代替手段を提供します。

例 3: ドキュメントの検索と置換
Optional<User> result = template.update(Person.class)      (1)
    .matching(query(where("firstame").is("Tom")))          (2)
    .replaceWith(new Person("Dick"))
    .withOptions(FindAndReplaceOptions.options().upsert()) (3)
    .as(User.class)                                        (4)
    .findAndReplace();                                     (5)
1 クエリのマッピングとコレクション名の導出に指定されたドメイン型で Fluent Update API を使用するか、単に MongoOperations#findAndReplace を使用します。
2 指定されたドメイン型に対してマッピングされた実際の一致クエリ。クエリを介して sortfieldscollation 設定を提供します。
3upsert など、デフォルト以外のオプションを提供する追加のオプションのフック。
4 操作結果のマッピングに使用されるオプションの射影型。何も指定されていない場合は、初期ドメイン型が使用されます。
5 実際の処理をトリガーします。null 許容の結果を取得するには、Optional の代わりに findAndReplaceValue を使用します。
既存の Document の id はストア自体によって交換品に引き継がれるため、交換品は id 自体を保持してはいけないことに注意してください。また、findAndReplace は、指定された並べ替え順序に応じて、クエリ条件に一致する最初のドキュメントのみを置き換えることにも注意してください。

削除

5 つのオーバーロードされたメソッドのいずれかを使用して、データベースからオブジェクトを削除できます。

template.remove(tywin, "GOT");                                              (1)

template.remove(query(where("lastname").is("lannister")), "GOT");           (2)

template.remove(new Query().limit(3), "GOT");                               (3)

template.findAllAndRemove(query(where("lastname").is("lannister"), "GOT");  (4)

template.findAllAndRemove(new Query().limit(3), "GOT");                     (5)
1_id で指定された単一のエンティティを、関連付けられたコレクションから削除します。
2 クエリの条件に一致するすべてのドキュメントを GOT コレクションから削除します。
3GOT コレクションの最初の 3 つのドキュメントを削除します。<2> とは異なり、削除するドキュメントは _id によって識別され、指定されたクエリを実行し、最初に sortlimitskip オプションを適用してから、別の手順ですべてを一度に削除します。
4 クエリの条件に一致するすべてのドキュメントを GOT コレクションから削除します。<3> とは異なり、ドキュメントは一括で削除されず、1 つずつ削除されます。
5GOT コレクションの最初の 3 つのドキュメントを削除します。<3> とは異なり、ドキュメントは一括で削除されず、1 つずつ削除されます。

楽観的ロック

@Version アノテーションは、MongoDB のコンテキストで JPA と同様の構文を提供し、バージョンが一致するドキュメントにのみ更新が適用されるようにします。バージョンプロパティの実際の値は、その間に別の操作でドキュメントが変更された場合でも更新が影響を及ぼさないように更新クエリに追加されます。その場合、OptimisticLockingFailureException がスローされます。次の例は、これらの機能を示しています。

@Document
class Person {

  @Id String id;
  String firstname;
  String lastname;
  @Version Long version;
}

Person daenerys = template.insert(new Person("Daenerys"));                            (1)

Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class); (2)

daenerys.setLastname("Targaryen");
template.save(daenerys);                                                              (3)

template.save(tmp); // throws OptimisticLockingFailureException                       (4)
1 最初にドキュメントを挿入します。version は 0 に設定されます。
2 挿入したばかりのドキュメントを読み込みます。version は 0 のままです。
3version = 0 でドキュメントを更新します。lastname を設定し、version を 1 にバンプします。
4version = 0 がまだある、以前にロードされたドキュメントを更新してみてください。現在の version は 1 であるため、操作は OptimisticLockingFailureException で失敗します。

MongoTemplate 上の特定の CRUD 操作のみがバージョンプロパティを考慮して変更します。詳細については、MongoOperations java のドキュメントを参照してください。

オプティミスティックロックでは、WriteConcern を ACKNOWLEDGED に設定する必要があります。そうしないと、OptimisticLockingFailureException が静かに飲み込まれる可能性があります。
バージョン 2.2 以降、データベースからエンティティを削除する場合、MongoOperations には @Version プロパティも含まれます。バージョンチェックを行わずに Document を削除するには、MongoOperations#remove(Object) の代わりに MongoOperations#remove(Query,…​) を使用します。
バージョン 2.2 以降、リポジトリは、バージョン管理されたエンティティを削除するときに、承認された削除の結果をチェックします。OptimisticLockingFailureException は、バージョン管理されたエンティティを CrudRepository.delete(Object) を通じて削除できない場合に発生します。このような場合は、その間にバージョンが変更されたか、オブジェクトが削除されました。CrudRepository.deleteById(ID) を使用すると、オプティミスティックロック機能をバイパスし、バージョンに関係なくオブジェクトを削除できます。