此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data MongoDB 4.4.0! |
保存、更新和删除文档
MongoTemplate
/ ReactiveMongoTemplatge
允许您保存、更新和删除域对象,并将这些对象映射到存储在 MongoDB 中的文档。
命令式 API 和反应式 API 的 API 签名基本相同,只是返回类型不同。
同步 API 使用 , single 而反应式 API 由 、 和 组成。void
Object
List
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
-
Imperative
-
Reactive
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
通过识别(通过约定)属性名称,在 a 和 an 之间实现数据库中存储的隐式转换。String
ObjectId
Id
前面的示例旨在显示 / 上 save、update 和 remove 操作的使用,而不是显示复杂的映射功能。
前面示例中使用的查询语法在 “查询文档” 一节中有更详细的说明。MongoTemplate
ReactiveMongoTemplate
MongoDB 要求您有一个用于所有文档的字段。请参阅 ID 处理部分,了解有关此字段的特殊处理的详细信息。_id |
MongoDB 集合可以包含表示各种类型实例的文档。有关详细信息,请参阅类型映射。 |
插入 / 保存
有几种方便的方法可以保存和插入您的对象。
为了对转换过程进行更精细的控制,您可以使用 — 例如 和 注册 Spring 转换器。MongoTemplate
MappingMongoConverter
Converter<Person, Document>
Converter<Document, Person>
插入操作和保存操作之间的区别在于,如果对象尚不存在,则保存操作将执行插入。 |
使用 save 操作的简单情况是保存一个 POJO。 在这种情况下,集合名称由类的名称(非完全限定)确定。 您还可以使用特定的集合名称调用 save 操作。您可以使用映射元数据覆盖用于存储对象的集合。
在插入或保存时,如果未设置该属性,则假定其值将由数据库自动生成。
因此,要成功自动生成 an,类中属性或字段的类型必须是 、 、 或 。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());
可以使用以下 insert 和 save 操作:
-
void
save :将对象保存到默认集合。(Object objectToSave)
-
void
save :将对象保存到指定的集合中。(Object objectToSave, String collectionName)
也可以使用一组类似的 insert 操作:
-
void
insert :将对象插入默认集合。(Object objectToSave)
-
void
insert :将对象插入到指定的集合中。(Object objectToSave, String collectionName)
如何在映射图层中处理字段_id
MongoDB 要求您有一个用于所有文档的字段。
如果您不提供标识符,驱动程序将分配一个 with generated value,而不会考虑您的域模型,因为服务器不知道您的标识符类型。
使用 时,某些规则控制如何将 Java 类中的属性映射到此字段:_id
ObjectId
MappingMongoConverter
_id
-
使用 () 批注的属性或字段映射到该字段。
@Id
org.springframework.data.annotation.Id
_id
-
没有注释但已命名的属性或字段映射到该字段。
id
_id
下面概述了在使用 (默认为 )时对映射到 document 字段的属性执行的类型转换(如果有)。_id
MappingMongoConverter
MongoTemplate
-
如果可能,使用 Spring 将 Java 类中声明为 a 的属性或字段转换为 an 并存储为 an 。有效的转换规则将委托给 MongoDB Java 驱动程序。如果无法转换为 an ,则该值将作为字符串存储在数据库中。
id
String
ObjectId
Converter<String, ObjectId>
ObjectId
-
在 Java 类中声明的属性或字段使用 Spring 转换为并存储为 an 。
id
BigInteger
ObjectId
Converter<BigInteger, ObjectId>
如果 Java 类中不存在前几组规则中指定的字段或属性,则驱动程序将生成一个隐式文件,但不会映射到 Java 类的属性或字段。_id
在查询和更新时,使用与上述规则对应的转换器来保存文档,以便查询中使用的字段名称和类型可以匹配域类中的内容。MongoTemplate
某些环境需要一种自定义方法来映射值,例如存储在 MongoDB 中的数据,这些数据未通过 Spring Data 映射层运行。文档可以包含可以表示为 或 的值。
将文档从存储读取回域类型工作正常。由于隐式转换,通过 their 查询文档可能很麻烦。因此,无法以这种方式检索文档。
对于这些情况,可以更好地控制实际的 ID 映射尝试。Id
_id
ObjectId
String
id
ObjectId
@MongoId
@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 是有效的十六进制,否则视为 .对应于用法。ObjectId String ObjectId String @Id |
我的文档将保存到哪个集合中?
有两种方法可以管理用于文档的集合名称。
使用的默认集合名称是更改为以小写字母开头的类名。
因此,类存储在集合中。
您可以通过在注释中提供不同的集合名称来自定义此名称。
您还可以通过提供自己的集合名称作为所选方法调用的最后一个参数来覆盖集合名称。com.test.Person
person
@Document
MongoTemplate
插入或保存单个对象
MongoDB 驱动程序支持在单个操作中插入文档集合。
接口中的以下方法支持此功能:MongoOperations
-
insert:插入对象。如果存在具有相同 的现有文档,则会生成错误。
id
-
insertAll:将 of objects 作为第一个参数。此方法根据前面指定的规则检查每个对象并将其插入到相应的集合中。
Collection
-
save:保存对象,覆盖可能具有相同 .
id
在批处理中插入多个对象
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();
批处理和批量的服务器性能相同。 但是,批量操作不会发布生命周期事件。 |
在调用 insert 之前未设置的任何属性都将使用 (在像 这样的简单类型的情况下) 或包装器类型(例如 )自动初始化。 |
更新
对于更新,您可以使用 using 更新找到的第一个文档,也可以使用该方法或 Fluent API 更新找到与查询匹配的所有文档。
以下示例显示了我们使用运算符向余额添加一次性 $50.00 奖金的所有账户的更新:MongoOperation.updateFirst
MongoOperation.updateMulti
all
SAVINGS
$inc
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();
除了前面讨论的内容之外,我们还使用对象提供更新定义。
该类具有与 MongoDB 可用的 update 修饰符匹配的方法。
大多数方法返回对象,以便为 API 提供 Fluent 样式。Query
Update
Update
Update
|
运行文档更新的方法
-
updateFirst:使用更新的文档更新与查询文档条件匹配的第一个文档。
-
updateMulti:使用更新的文档更新与查询文档条件匹配的所有对象。
updateFirst 不支持排序。请使用 findAndModify 来申请 。Sort |
更新操作的索引提示可以通过以下方式提供。Query.withHint(…) |
类中的方法Update
你可以在类中使用一点 “'syntax sugar'”,因为它的方法应该链接在一起。
此外,您还可以通过使用静态导入来启动新实例的创建。Update
Update
public static Update update(String key, Object value)
该类包含以下方法:Update
-
Update
addToSet 使用 update 修饰符进行更新(String key, Object value)
$addToSet
-
Update
当前日期 使用 update 修饰符进行更新(String key)
$currentDate
-
Update
当前时间戳 使用 update 修饰符进行更新(String key)
$currentDate
$type
timestamp
-
Update
公司 使用 update 修饰符进行更新(String key, Number inc)
$inc
-
Update
麦克斯 使用 update 修饰符进行更新(String key, Object max)
$max
-
Update
分钟 使用 update 修饰符进行更新(String key, Object min)
$min
-
Update
乘 使用 update 修饰符进行更新(String key, Number multiplier)
$mul
-
Update
流行 使用 update 修饰符进行更新(String key, Update.Position pos)
$pop
-
Update
拉 使用 update 修饰符进行更新(String key, Object value)
$pull
-
Update
拉全部 使用 update 修饰符进行更新(String key, Object[] values)
$pullAll
-
Update
推 使用 update 修饰符进行更新(String key, Object value)
$push
-
Update
全部 使用 update 修饰符进行更新(String key, Object[] values)
$pushAll
-
Update
重命名 使用 update 修饰符进行更新(String oldName, String newName)
$rename
-
Update
设置 使用 update 修饰符进行更新(String key, Object value)
$set
-
Update
setOnInsert 使用 update 修饰符进行更新(String key, Object value)
$setOnInsert
-
Update
未凝固的 使用 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");
聚合管道更新
更新由 Aggregation Pipeline 公开的方法,并通过 .
使用允许在更新操作中利用 MongoDB 4.2 聚合。
在更新中使用聚合允许通过使用单个操作表示多个阶段和多个条件来更新一个或多个字段。MongoOperations
ReactiveMongoOperations
AggregationUpdate
AggregationUpdate
更新可以包括以下阶段:
-
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 阶段根据 tests 字段的平均值计算新的字段平均值。$set |
2 | 第 2 阶段根据第一个聚合阶段计算的平均字段计算新的字段等级。$set |
3 | 该管道在 students 集合上运行,并用于聚合字段映射。Student |
4 | 将更新应用于集合中的所有匹配文档。 |
Upsert
与执行操作相关,您还可以执行操作,如果未找到与查询匹配的文档,则将执行插入。
插入的文档是 query 文档和 update 文档的组合。
以下示例演示如何使用该方法: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 |
|
替换集合中的文档
通过 allow 提供的各种方法允许覆盖第一个匹配的 Document。
如果未找到匹配项,则可以通过提供相应的配置来更新插入新的匹配项(如上一节所述)。replace
MongoTemplate
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 | 插入新文档。 |
2 | 用于标识要替换的单个文档的查询。 |
3 | 设置替换文档,该文档必须与现有文档相同或根本没有。_id _id |
4 | 运行 replace 操作。 .将一个替换为 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,否则MongoDB 将创建一个新的 possible with domain type incompatible 。
由于 MongoDB 不知道您的域类型,因此不会考虑任何提示,并且结果可能与您的域模型不兼容。_id ObjectId @Field(targetType) ObjectId |
2 | 如果未找到匹配项,则用于插入新文档upsert |
无法通过 replace 操作更改现有文档的 。
在 MongoDB 上使用 2 种方法来确定条目的新 ID:
* The 在查询中使用,如 * The 存在于替换文档中。
如果以任何一种方式提供 no,MongoDB 将为文档创建一个新的。
如果 used domain types 属性具有不同的类型,这可能会导致映射和数据查找失败,例如 . |
查找和修改
方法 on 可以更新文档,并在单个操作中返回旧文档或新更新的文档。 提供了四个重载的方法,它们接受 AND 类并转换为 POJO:findAndModify(…)
MongoCollection
MongoTemplate
findAndModify
Query
Update
Document
<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()
|
查找和替换
替换整个最直接的方法是使用该方法。
但是,这可能并不总是可行的。 提供了一种替代方法,允许通过简单的查询来标识要替换的文档。Document
id
save
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 | 将 Fluent Update API 与给定的域类型一起使用,以映射查询和派生集合名称,或者只使用 。MongoOperations#findAndReplace |
2 | 针对给定域类型映射的实际匹配查询。通过查询提供 和 设置。sort fields collation |
3 | 其他可选钩子,用于提供默认值以外的选项,例如 .upsert |
4 | 用于映射操作结果的可选投影类型。如果没有,则使用初始域类型。 |
5 | 触发实际处理。用于获取可为 null 的结果,而不是 .findAndReplaceValue Optional |
请注意,替换件不得像现有的那样保持自身
由商店本身结转到替代品。另请记住,这只会替换第一个
document 匹配查询条件,具体取决于可能给定的排序顺序。id id Document findAndReplace |
删除
您可以使用以下五种重载方法之一从数据库中删除对象:
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 | 删除集合中的前三个文档。与 <2> 不同,要删除的文档由其 、 运行给定的查询、首先应用 、 和 选项,然后在单独的步骤中一次删除所有文档来标识。GOT _id sort limit skip |
4 | 从集合中删除与查询条件匹配的所有文档。与 <3> 不同,文档不会批量删除,而是逐个删除。GOT |
5 | 删除集合中的前三个文档。与 <3> 不同,文档不会批量删除,而是逐个删除。GOT |
乐观锁定
该注释提供的语法类似于 MongoDB 上下文中的 JPA 语法,并确保更新仅应用于具有匹配版本的文档。
因此,将 version 属性的实际值添加到 update 查询中,如果另一个操作同时更改了文档,则 update 不会产生任何影响。
在这种情况下,将引发 an。
以下示例显示了这些功能:@Version
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 |
3 | 使用 更新文档。将 and bump 设置为 。version = 0 lastname version 1 |
4 | 尝试更新以前加载的仍具有 .操作失败并显示 ,因为电流为 。version = 0 OptimisticLockingFailureException version 1 |
只有某些 CRUD 操作 do consider 和 alter version 属性。有关详细信息,请参阅 java doc。MongoTemplate
MongoOperations
乐观锁定需要将 设置为 。否则可静默吞服。WriteConcern ACKNOWLEDGED OptimisticLockingFailureException |
从 Version 2.2 开始,从数据库中删除实体时还包括该属性。
要删除没有版本检查的 ,请使用 而不是 .MongoOperations @Version Document MongoOperations#remove(Query,…) MongoOperations#remove(Object) |
从版本 2.2 开始,存储库在删除版本控制实体时检查确认删除的结果。
如果无法通过 .在这种情况下,版本已更改或同时删除了对象。用于绕过乐观锁定功能并删除对象,而不管其版本如何。OptimisticLockingFailureException CrudRepository.delete(Object) CrudRepository.deleteById(ID) |