Saving, Updating, and Removing Documents
MongoTemplate
/ ReactiveMongoTemplatge
允许你保存、更新和删除你的领域对象,并将这些对象映射到存储在 MongoDB 中的文档。命令式 API 和响应式 API 的 API 签名主要相同,仅返回类型不同。同步 API 使用 void
、单个 Object
和 List
,而响应式对应项由 Mono<Void>
、Mono<Object>
和 Flux
组成。
考虑以下类:
Unresolved include directive in modules/ROOT/pages/mongodb/template-crud-operations.adoc - include::example$example/Person.java[]
给定前面示例中的 Person
类,你可以保存、更新和删除对象,如下所示示例:
-
Imperative
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]
- Reactive
-
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
的保存、更新和删除操作的使用,而不是显示复杂映射功能。在前面的示例中使用的查询语法将在 “Querying Documents” 部分进行更详细的说明。
MongoDB 要求您为所有文档设置一个`_id` 字段。有关此字段的特殊处理的详细信息,请参阅ID handling 部分。
MongoDB 集合可以包含表示各种类型实例的文档。有关详细信息,请参阅type mapping。
Insert / Save
MongoTemplate
上有几种方便的方法,用于保存和插入你的对象。要对转换过程有更细粒度的控制,你可以使用 MappingMongoConverter
注册 Spring 转换器,例如 Converter<Person, Document>
和 Converter<Document, Person>
。
插入和保存操作之间的差别在于,如果对象不存在,保存操作将执行插入操作。 |
使用保存操作的简单情况是保存 POJO。在这种情况下,集合名称由类的名称(不完全限定)确定。你也可以使用特定集合名称调用保存操作。你可以使用映射元数据来覆盖要存储对象的集合。
在插入或保存时,如果未设置 Id
属性,则假设其值将由数据库自动生成。因此,要成功自动生成 ObjectId
,类中 Id
属性或字段的类型必须是 String
、ObjectId
或 BigInteger
。
以下示例演示了如何保存文档并检索其内容:
-
Imperative
-
Reactive
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)
: 将对象插入到指定集合。
How the _id
Field is Handled in the Mapping Layer
MongoDB 要求所有文档都有一个 _id
字段。如果你没有提供,则驱动程序会分配一个带有生成值的 ObjectId
,而不考虑你的领域模型,因为服务器不知道你的标识符类型。当你使用 MappingMongoConverter
时,某些规则会控制如何将 Java 类的属性映射到此 _id
字段:
-
用
@Id
(org.springframework.data.annotation.Id
) 注释的属性或字段映射到_id
字段。 -
一个没有注释但名为
id
的属性或字段映射到_id
字段。
以下内容概述了使用 MappingMongoConverter
(MongoTemplate
的默认值)时对映射到 _id
文档字段的属性进行的类型转换(如有)。
-
如果可能,在 Java 类中声明为
String
的id
属性或字段将通过使用 SpringConverter<String, ObjectId>
转换并存储为ObjectId
。有效转换规则委托给 MongoDB Java 驱动程序。如果无法转换为ObjectId
,则该值将作为字符串存储在数据库中。 -
在 Java 类中声明为
BigInteger
的id
属性或字段将通过使用 SpringConverter<BigInteger, ObjectId>
转换并存储为ObjectId
。
如果 Java 类中不存在以前各组规则中指定字段或属性,则驱动程序会生成一个隐式的 _id
文件,但不将其映射到 Java 类的属性或字段。
在查询和更新时,MongoTemplate
使用与保存文档的前述规则相对应的转换器,以便查询中使用的字段名称和类型可以与你的域类中的保持一致。
某些环境需要一种自定义方法来映射 Id
值,例如存储在未通过 Spring 数据映射层运行的 MongoDB 中的数据。文档可以包含可表示为 ObjectId
或 String
的 _id
值。将文档从存储中读回到域类型时工作得很好。由于隐式 ObjectId
转换,通过其 id
查询文档可能很麻烦。因此,无法以这种方式检索文档。对于这些情况,@MongoId
对实际 ID 映射尝试提供了更多控制。
@MongoId
mappingpublic 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 | 如果给定的 String 是有效的 ObjectId 十六进制,则将 id视为 ObjectId ,否则视为 String 。对应于 @Id 用法。 |
Into Which Collection Are My Documents Saved?
有两种方法可以管理用于文档的集合名称。使用的默认集合名称是类名称更改为以小写字母开头。因此,com.test.Person
类存储在 person
集合中。可以通过使用 @Document
注释提供不同的集合名称来对其进行自定义。你还可以通过为所选 MongoTemplate
方法调用提供自己的集合名称作为最后一个参数来覆盖集合名称。
Inserting or Saving Individual Objects
MongoDB 驱动程序支持通过单个操作插入文档集合。MongoOperations
接口中的以下方法支持此功能:
-
insert: 插入一个对象。如果存在具有相同
id
的现有文档,则会生成错误。 -
insertAll: 将一组对象作为第一个参数。此方法会检查每个对象,并根据前面指定的规则将其插入到相应的集合中。
-
save: 保存对象,覆盖可能具有相同
id
的任何对象。
Inserting Several Objects in a Batch
MongoDB 驱动程序支持通过一个操作插入文档集合。MongoOperations
接口中的以下方法通过 insert
或专用 BulkOperations
接口支持此功能。
-
Imperative
-
Reactive
Collection<Person> inserted = template.insert(List.of(...), Person.class);
Flux<Person> inserted = template.insert(List.of(...), Person.class);
-
Imperative
-
Reactive
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();
批处理和批量处理的服务器性能是相同的。但是,批量操作不发布 lifecycle events。 |
任何在调用 insert 之前还未设置的 @Version
属性将使用 1
(对于 int
等简单类型)自动初始化或在包装器类型(例如 Integer
)中使用 0
自动初始化。请在 Optimistic Locking 部分中了解更多信息。
Update
对于更新,你可以使用 MongoOperation.updateFirst
更新找到的第一个文档,也可以使用 MongoOperation.updateMulti
方法或 fluent API 上的 all
更新找到与查询匹配的所有文档。下面的示例显示了更新所有 SAVINGS
帐户,其中我们使用 $inc
运算符为余额添加一次性 $50.00 奖励:
MongoTemplate
/ ReactiveMongoTemplate
-
Imperative
-
Reactive
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 提供 fluent 样式。
如果 @Version
属性未包含在 Update
中,则会自动对其进行递增。请在 Optimistic Locking 部分中了解更多信息。
Methods for Running Updates for Documents
-
updateFirst: 使用更新后的文档更新与查询文档 criteria 匹配的第一个文档。
-
updateMulti: 使用更新后的文档更新所有与查询文档 criteria 匹配的对象。
updateFirst
不支持排序。请使用findAndModify 来应用`Sort`。
可以经由 |
Methods in the Update
Class
你可以对 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)`使用带 `$type``timestamp
的 `$currentDate`更新修饰符进行更新 -
Update
inc`(String key, Number inc)`使用 `$inc`更新修饰符进行更新 -
Update
max`(String key, Object max)`使用 `$max`更新修饰符进行更新 -
Update
min`(String key, Object min)`使用 `$min`更新修饰符进行更新 -
Update
multiply`(String key, Number multiplier)`使用 `$mul`更新修饰符进行更新 -
Update
pop`(String key, Update.Position pos)`使用 `$pop`更新修饰符进行更新 -
Update
pull`(String key, Object value)`使用 `$pull`更新修饰符进行更新 -
Update
pullAll`(String key, Object[] values)`使用 `$pullAll`更新修饰符进行更新 -
Update
push`(String key, Object value)`使用 `$push`更新修饰符进行更新 -
Update
pushAll`(String key, Object[] values)`使用 `$pushAll`更新修饰符进行更新 -
Update
rename`(String oldName, String newName)`使用 `$rename`更新修饰符进行更新 -
Update
set`(String key, Object value)`使用 `$set`更新修饰符进行更新 -
Update
setOnInsert`(String key, Object value)`使用 `$setOnInsert`更新修饰符进行更新 -
Update
unset`(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");
Aggregation Pipeline Updates
由 MongoOperations
和 ReactiveMongoOperations
公开的更新方法也通过 AggregationUpdate
接受 Aggregation Pipeline。使用 AggregationUpdate
允许在更新操作中利用 MongoDB 4.2 aggregations。在更新中使用聚合允许通过使用单个操作来表达多个阶段和多个条件,从而更新一个或多个字段。
更新可以包含以下阶段:
-
AggregationUpdate.set(…​).toValue(…​)
→$set : { …​ }
-
AggregationUpdate.unset(…​)
→$unset : [ …​ ]
-
AggregationUpdate.replaceWith(…​)
→$replaceWith : { …​ }
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 | 第 1 个 $set 阶段根据 tests 字段的平均值计算一个新字段 average。 |
2 | 第 2 个 $set 阶段根据第一聚合阶段计算的 average 字段计算一个新字段 grade。 |
3 | 该管道运行于 students 集合,使用 Student 进行聚合字段映射。 |
4 | 将更新应用到集合中所有匹配的文档。 |
Upsert
与执行 updateFirst
操作相关,你还可以执行 upsert
操作,如果未找到与查询匹配的文档,则将执行插入操作。插入的文档是查询文档和更新文档的组合。下面的示例显示了如何使用 upsert
方法:
-
Imperative
-
Reactive
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
不支持排序。请使用 findAndModify 来申请 Sort
。
如果 @Version
属性中未包含 Update
,则会自动对其进行初始化。请在 Optimistic Locking 部分中了解更多信息。
Replacing Documents in a Collection
通过 MongoTemplate
提供的各种 replace
方法允许覆盖第一个匹配的文档。如果没有找到匹配项,可以通过提供带有相应配置的 ReplaceOptions
来插入新匹配项(如上一部分中所述)。
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 | 用于识别需要替换的单个文档的查询。 |
3 | 设置替换文档,其中必须包含与现有 _id 相同的 _id 或根本没有 _id 。 |
4 | 运行替换操作..用 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 | 对于 upsert,_id 值必须是存在的,否则 MongoDB 将创建一个新的,可能会与域类型不匹配的 ObjectId 。由于 MongoDB 无法识别你的域类型,所以不会考虑任何 @Field(targetType) 提示,而产生的 ObjectId 可能与你的域模型不兼容。 |
2 | 如果找不到匹配项,请使用 upsert 插入新文档 |
不可能通过替换操作更改现有文档的 _id
。对于 upsert
,MongoDB 使用两种方法来确定条目的新 ID:* _id
在查询中以 {"_id" : 1234 }
这种方式使用* _id
显示在替换文档中如果不以任何方式提供 _id
,MongoDB 将为该文档创建一个新的 ObjectId
。如果所使用的域类型 id
属性具有 Long
等不同类型,则这可能导致映射和数据查找出现故障。
Find and Modify
MongoCollection
上的 findAndModify(…)
方法可以在一次操作中更新文档并返回旧文档或新更新的文档。MongoTemplate
提供了四种重载的 findAndModify
方法,它们采用 Query
和 Update
类,并将 Document
转换为您的 POJO:
<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
方法允许您设置 returnNew
、upsert
和 remove
的选项。示例延续自上一个代码片段,如下所示:
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
中,则会自动对其进行递增。请在 Optimistic Locking 部分中了解更多信息。
Find and Replace
替换整个 Document
的最直接方法是使用 save
方法通过其 id
进行替换。但这可能并不总可行。findAndReplace
提供了一种替代方案,允许通过简单的查询来识别要替换的文档。
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 | 使用具有给定域类型的流畅更新 API 来映射查询并获取集合名称,或者仅使用 MongoOperations#findAndReplace 。 |
2 | 针对给定域类型映射的实际匹配查询。通过该查询提供 sort 、fields 和 collation 设置。 |
3 | 用于提供默认值之外的其他选项的附加可选挂钩,如 upsert 。 |
4 | 用于映射操作结果的可选投影类型。如果未给出,将使用初始域类型。 |
5 | 触发实际处理。使用 findAndReplaceValue 获得可为 null 的结果,而不是 Optional 。 |
请注意,替换本身不能包含 id
,因为现有 Document
的 id
由存储本身已转到替换中。还要记住,findAndReplace
只会替换第一个与查询标准匹配的文档,具体取决于可能提供的排序顺序。
Delete
您可以使用五种重载方法之一从数据库中删除对象:
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 集合中移除所有与查询条件匹配的文档。 |
3 | 移除 GOT 集合中的前三个文档。与 <2> 不同的是,要移除的文档由其 _id 标识,先运行给定的查询,应用 sort 、limit 和 skip 选项,然后分批移除所有文档。 |
4 | 从 GOT 集合中移除所有与查询条件匹配的文档。与 <3> 不同的是,文档不是批量删除,而是逐个删除。 |
5 | 移除 GOT 集合中的前三个文档。与 <3> 不同的是,文档不是批量删除,而是逐个删除。 |
Optimistic Locking
@Version
注释提供了类似于 JPA 在 MongoDB 上下文中的语法,并确保仅将更新应用于具有匹配版本的文档。因此,在将版本属性的实际值添加到更新查询时,如果其他操作在此期间更改了文档,则更新不会产生任何影响。在这种情况下,将引发 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 | 1. 最初插入文档。version 设置为 0 。 |
2 | 2. 加载刚刚插入的文档。version 仍然是 0 。 |
3 | 使用 version = 0 更新文档。设置 lastname ,并将 version 增加到 1 。 |
4 | 尝试更新之前加载的仍具有 version = 0 的文档。该操作失败,并带有 OptimisticLockingFailureException ,因为当前 version 为 1 。 |
只有 MongoTemplate
上的某些 CRUD 操作会考虑和更改版本属性。请查阅 MongoOperations
Java 文档以获取详细信息。
乐观锁定要求将 WriteConcern
设置为 ACKNOWLEDGED
。否则 OptimisticLockingFailureException
可能被悄悄吸收。
从 2.2 版本开始, |
从 2.2 版本开始,储存库会在删除版本化实体时,检查已确认删除的结果。如果无法通过 |