对于最新的稳定版本,请使用 Spring Data MongoDB 4.4.0spring-doc.cn

保存、更新和删除文档

MongoTemplate / ReactiveMongoTemplatge允许您保存、更新和删除域对象,并将这些对象映射到存储在 MongoDB 中的文档。 命令式 API 和反应式 API 的 API 签名基本相同,只是返回类型不同。 同步 API 使用 , single 而反应式 API 由 、 和 组成。voidObjectListMono<Void>Mono<Object>Fluxspring-doc.cn

请考虑以下类:spring-doc.cn

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 + "]";
	}
}

给定前面示例中的类,您可以保存、更新和删除对象,如下例所示:Personspring-doc.cn

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);
  }
}

前面的示例将生成以下日志输出(包括来自):MongoTemplatespring-doc.cn

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 caused implicit conversion between a and an stored in the database by recognizing (through convention) the property name.StringObjectIdIdspring-doc.cn

The preceding example is meant to show the use of save, update, and remove operations on / and not to show complex mapping functionality. The query syntax used in the preceding example is explained in more detail in the section “Querying Documents”.MongoTemplateReactiveMongoTemplatespring-doc.cn

MongoDB requires that you have an field for all documents. Please refer to the ID handling section for details on the special treatment of this field. _id
MongoDB collections can contain documents that represent instances of a variety of types. Please refer to the type mapping for details.

Insert / Save

There are several convenient methods on for saving and inserting your objects. To have more fine-grained control over the conversion process, you can register Spring converters with the  — for example and .MongoTemplateMappingMongoConverterConverter<Person, Document>Converter<Document, Person>spring-doc.cn

插入操作和保存操作之间的区别在于,如果对象尚不存在,则保存操作将执行插入。

使用 save 操作的简单情况是保存一个 POJO。 在这种情况下,集合名称由类的名称(非完全限定)确定。 您还可以使用特定的集合名称调用 save 操作。您可以使用映射元数据覆盖用于存储对象的集合。spring-doc.cn

在插入或保存时,如果未设置该属性,则假定其值将由数据库自动生成。 因此,要成功自动生成 an,类中属性或字段的类型必须是 、 、 或 。IdObjectIdIdStringObjectIdBigIntegerspring-doc.cn

以下示例显示如何保存文档并检索其内容:spring-doc.cn

使用 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());

可以使用以下 insert 和 save 操作:spring-doc.cn

  • void save :将对象保存到默认集合。(Object objectToSave)spring-doc.cn

  • void save :将对象保存到指定的集合中。(Object objectToSave, String collectionName)spring-doc.cn

也可以使用一组类似的 insert 操作:spring-doc.cn

  • void insert :将对象插入默认集合。(Object objectToSave)spring-doc.cn

  • void insert :将对象插入到指定的集合中。(Object objectToSave, String collectionName)spring-doc.cn

如何在映射图层中处理字段_id

MongoDB 要求您有一个用于所有文档的字段。 如果您不提供标识符,驱动程序将分配一个 with generated value,而不会考虑您的域模型,因为服务器不知道您的标识符类型。 使用 时,某些规则控制如何将 Java 类中的属性映射到此字段:_idObjectIdMappingMongoConverter_idspring-doc.cn

  1. 使用 () 批注的属性或字段映射到该字段。@Idorg.springframework.data.annotation.Id_idspring-doc.cn

  2. 没有注释但已命名的属性或字段映射到该字段。id_idspring-doc.cn

下面概述了在使用 (默认为 )时对映射到 document 字段的属性执行的类型转换(如果有)。_idMappingMongoConverterMongoTemplatespring-doc.cn

  1. 如果可能,使用 Spring 将 Java 类中声明为 a 的属性或字段转换为 an 并存储为 an 。有效的转换规则将委托给 MongoDB Java 驱动程序。如果无法转换为 an ,则该值将作为字符串存储在数据库中。idStringObjectIdConverter<String, ObjectId>ObjectIdspring-doc.cn

  2. 在 Java 类中声明的属性或字段使用 Spring 转换为并存储为 an 。idBigIntegerObjectIdConverter<BigInteger, ObjectId>spring-doc.cn

如果 Java 类中不存在前几组规则中指定的字段或属性,则驱动程序将生成一个隐式文件,但不会映射到 Java 类的属性或字段。_idspring-doc.cn

在查询和更新时,使用与上述规则对应的转换器来保存文档,以便查询中使用的字段名称和类型可以匹配域类中的内容。MongoTemplatespring-doc.cn

某些环境需要一种自定义方法来映射值,例如存储在 MongoDB 中的数据,这些数据未通过 Spring Data 映射层运行。文档可以包含可以表示为 或 的值。 将文档从存储读取回域类型工作正常。由于隐式转换,通过 their 查询文档可能很麻烦。因此,无法以这种方式检索文档。 对于这些情况,可以更好地控制实际的 ID 映射尝试。Id_idObjectIdStringidObjectId@MongoIdspring-doc.cn

示例 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)
}
1 该 ID 被视为无需进一步转换。String
2 id 被视为 .ObjectId
3 id 被视为 given 是有效的十六进制,否则视为 .对应于用法。ObjectIdStringObjectIdString@Id

我的文档将保存到哪个集合中?

有两种方法可以管理用于文档的集合名称。 使用的默认集合名称是更改为以小写字母开头的类名。 因此,类存储在集合中。 您可以通过在注释中提供不同的集合名称来自定义此名称。 您还可以通过提供自己的集合名称作为所选方法调用的最后一个参数来覆盖集合名称。com.test.Personperson@DocumentMongoTemplatespring-doc.cn

插入或保存单个对象

MongoDB 驱动程序支持在单个操作中插入文档集合。 接口中的以下方法支持此功能:MongoOperationsspring-doc.cn

  • insert:插入对象。如果存在具有相同 的现有文档,则会生成错误。idspring-doc.cn

  • insertAll:将 of objects 作为第一个参数。此方法根据前面指定的规则检查每个对象并将其插入到相应的集合中。Collectionspring-doc.cn

  • save:保存对象,覆盖可能具有相同 .idspring-doc.cn

在批处理中插入多个对象

MongoDB 驱动程序支持在一个操作中插入文档集合。 接口中的以下方法通过 或 专用接口支持此功能。MongoOperationsinsertBulkOperationsspring-doc.cn

批量插入
Collection<Person> inserted = template.insert(List.of(...), Person.class);
Flux<Person> inserted = template.insert(List.of(...), Person.class);
Bulk Insert
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();

Server performance of batch and bulk is identical. However bulk operations do not publish lifecycle events.spring-doc.cn

Any property that has not been set prior to calling insert will be auto initialized with (in case of a simple type like ) or for wrapper types (eg. ).
Read more in the see Optimistic Locking section.
@Version1int0Integerspring-doc.cn

Update

For updates, you can update the first document found by using or you can update all documents that were found to match the query by using the method or on the fluent API. The following example shows an update of all accounts where we are adding a one-time $50.00 bonus to the balance by using the operator:MongoOperation.updateFirstMongoOperation.updateMultiallSAVINGS$incspring-doc.cn

Updating documents by using the 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();

In addition to the discussed earlier, we provide the update definition by using an object. The class has methods that match the update modifiers available for MongoDB. Most methods return the object to provide a fluent style for the API.QueryUpdateUpdateUpdatespring-doc.cn

@Version properties if not included in the will be automatically incremented. Read more in the see Optimistic Locking section.Updatespring-doc.cn

Methods for Running Updates for Documents

  • updateFirst: Updates the first document that matches the query document criteria with the updated document.spring-doc.cn

  • updateMulti: Updates all objects that match the query document criteria with the updated document.spring-doc.cn

updateFirst does not support ordering. Please use findAndModify to apply . Sort
Index hints for the update operation can be provided via . Query.withHint(…​)

Methods in the ClassUpdate

You can use a little "'syntax sugar'" with the class, as its methods are meant to be chained together. Also, you can kick-start the creation of a new instance by using and using static imports.UpdateUpdatepublic static Update update(String key, Object value)spring-doc.cn

The class contains the following methods:Updatespring-doc.cn

  • Update addToSet Update using the update modifier(String key, Object value)$addToSetspring-doc.cn

  • Update currentDate Update using the update modifier(String key)$currentDatespring-doc.cn

  • Update currentTimestamp Update using the update modifier with (String key)$currentDate$type timestampspring-doc.cn

  • Update inc Update using the update modifier(String key, Number inc)$incspring-doc.cn

  • Update max Update using the update modifier(String key, Object max)$maxspring-doc.cn

  • Update min Update using the update modifier(String key, Object min)$minspring-doc.cn

  • Update multiply Update using the update modifier(String key, Number multiplier)$mulspring-doc.cn

  • Update pop Update using the update modifier(String key, Update.Position pos)$popspring-doc.cn

  • Update pull Update using the update modifier(String key, Object value)$pullspring-doc.cn

  • Update pullAll Update using the update modifier(String key, Object[] values)$pullAllspring-doc.cn

  • Update push Update using the update modifier(String key, Object value)$pushspring-doc.cn

  • Update pushAll Update using the update modifier(String key, Object[] values)$pushAllspring-doc.cn

  • Update rename Update using the update modifier(String oldName, String newName)$renamespring-doc.cn

  • Update set Update using the update modifier(String key, Object value)$setspring-doc.cn

  • Update setOnInsert Update using the update modifier(String key, Object value)$setOnInsertspring-doc.cn

  • Update unset Update using the update modifier(String key)$unsetspring-doc.cn

Some update modifiers, such as and , allow nesting of additional operators.$push$addToSetspring-doc.cn

// { $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");

Aggregation Pipeline Updates

Update methods exposed by and also accept an Aggregation Pipeline via . Using allows leveraging MongoDB 4.2 aggregations in an update operation. Using aggregations in an update allows updating one or more fields by expressing multiple stages and multiple conditions with a single operation.MongoOperationsReactiveMongoOperationsAggregationUpdateAggregationUpdatespring-doc.cn

The update can consist of the following stages:spring-doc.cn

  • AggregationUpdate.set(…​).toValue(…​)$set : { …​ }spring-doc.cn

  • AggregationUpdate.unset(…​)$unset : [ …​ ]spring-doc.cn

  • AggregationUpdate.replaceWith(…​)$replaceWith : { …​ }spring-doc.cn

Example 2. Update Aggregation
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 The 1st stage calculates a new field average based on the average of the tests field.$set
2 The 2nd stage calculates a new field grade based on the average field calculated by the first aggregation stage.$set
3 The pipeline is run on the students collection and uses for the aggregation field mapping.Student
4 Apply the update to all matching documents in the collection.

Upsert

Related to performing an operation, you can also perform an operation, which will perform an insert if no document is found that matches the query. The document that is inserted is a combination of the query document and the update document. The following example shows how to use the method:updateFirstupsertupsertspring-doc.cn

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 does not support ordering. Please use findAndModify to apply . Sort

@Version properties if not included in the will be automatically initialized. Read more in the see Optimistic Locking section.Updatespring-doc.cn

Replacing Documents in a Collection

The various methods available via allow to override the first matching Document. If no match is found a new one can be upserted (as outlined in the previous section) by providing with according configuration.replaceMongoTemplateReplaceOptionsspring-doc.cn

Replace one
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 Insert a new document.
2 The query used to identify the single document to replace.
3 Set up the replacement document which must hold either the same as the existing or no at all._id_id
4 Run the replace operation. .Replace one with 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 The value needs to be present for upsert, otherwise MongoDB will create a new potentially with the domain type incompatible . As MongoDB is not aware of your domain type, any hints are not considered and the resulting might be not compatible with your domain model._idObjectId@Field(targetType)ObjectId
2 Use to insert a new document if no match is foundupsert

It is not possible to change the of existing documents with a replace operation. On MongoDB uses 2 ways of determining the new id for the entry: * The is used within the query as in * The is present in the replacement document. If no is provided in either way, MongoDB will create a new for the document. This may lead to mapping and data lookup malfunctions if the used domain types property has a different type like e.g. ._idupsert_id{"_id" : 1234 }_id_idObjectIdidLongspring-doc.cn

Find and Modify

The method on can update a document and return either the old or newly updated document in a single operation. provides four overloaded methods that take and classes and converts from to your POJOs:findAndModify(…)MongoCollectionMongoTemplatefindAndModifyQueryUpdateDocumentspring-doc.cn

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

The following example inserts a few objects into the container and performs a operation:PersonfindAndUpdatespring-doc.cn

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

The method lets you set the options of , , and . An example extending from the previous code snippet follows:FindAndModifyOptionsreturnNewupsertremovespring-doc.cn

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 properties if not included in the will be automatically incremented. Read more in the see Optimistic Locking section.Updatespring-doc.cn

Find and Replace

The most straight forward method of replacing an entire is via its using the method. However this might not always be feasible. offers an alternative that allows to identify the document to replace via a simple query.DocumentidsavefindAndReplacespring-doc.cn

Example 3. Find and Replace Documents
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 Use the fluent update API with the domain type given for mapping the query and deriving the collection name or just use .MongoOperations#findAndReplace
2 The actual match query mapped against the given domain type. Provide , and settings via the query.sortfieldscollation
3 Additional optional hook to provide options other than the defaults, like .upsert
4 An optional projection type used for mapping the operation result. If none given the initial domain type is used.
5 Trigger the actual processing. Use to obtain the nullable result instead of an .findAndReplaceValueOptional
Please note that the replacement must not hold an itself as the of the existing will be carried over to the replacement by the store itself. Also keep in mind that will only replace the first document matching the query criteria depending on a potentially given sort order. ididDocumentfindAndReplace

Delete

You can use one of five overloaded methods to remove an object from the database:spring-doc.cn

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 Remove a single entity specified by its from the associated collection._id
2 Remove all documents that match the criteria of the query from the collection.GOT
3 Remove the first three documents in the collection. Unlike <2>, the documents to remove are identified by their , running the given query, applying , , and options first, and then removing all at once in a separate step.GOT_idsortlimitskip
4 Remove all documents matching the criteria of the query from the collection. Unlike <3>, documents do not get deleted in a batch but one by one.GOT
5 Remove the first three documents in the collection. Unlike <3>, documents do not get deleted in a batch but one by one.GOT

Optimistic Locking

The annotation provides syntax similar to that of JPA in the context of MongoDB and makes sure updates are only applied to documents with a matching version. Therefore, the actual value of the version property is added to the update query in such a way that the update does not have any effect if another operation altered the document in the meantime. In that case, an is thrown. The following example shows these features:@VersionOptimisticLockingFailureExceptionspring-doc.cn

@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 Intially insert document. is set to .version0
2 Load the just inserted document. is still .version0
3 Update the document with . Set the and bump to .version = 0lastnameversion1
4 Try to update the previously loaded document that still has . The operation fails with an , as the current is .version = 0OptimisticLockingFailureExceptionversion1

Only certain CRUD operations on do consider and alter version properties. Please consult java doc for detailed information.MongoTemplateMongoOperationsspring-doc.cn

Optimistic Locking requires to set the to . Otherwise can be silently swallowed. WriteConcernACKNOWLEDGEDOptimisticLockingFailureException
As of Version 2.2 also includes the property when removing an entity from the database. To remove a without version check use instead of . MongoOperations@VersionDocumentMongoOperations#remove(Query,…​)MongoOperations#remove(Object)
As of Version 2.2 repositories check for the outcome of acknowledged deletes when removing versioned entities. An is raised if a versioned entity cannot be deleted through . In such case, the version was changed or the object was deleted in the meantime. Use to bypass optimistic locking functionality and delete objects regardless of their version. OptimisticLockingFailureExceptionCrudRepository.delete(Object)CrudRepository.deleteById(ID)