我们通过 Spring Data 使用 MongoDB,并依靠
findAndModify
操作来更新现有实体或创建新实体。
在
findAndModify
中,我们可以使用 returnNew(...)
配置返回实体的旧状态或新状态。
有没有办法从
findAndModify
返回新旧实体?
我们需要比较更新前后的实体状态,这就是我们需要两个实例的原因。
目前我们正在诉诸
requireNew(false)
,然后手动更新旧实例的副本,如下所示:
public Pair<Data> saveItems(String id, List<Item> items) {
final Query findById = ...;
final Update update = new Update();
// This is what we actually update
update.set(ENTITY_FIELD_ITEMS, newItems);
update.inc(ENTITY_FIELD_VERSION, 1);
// Try updating and return the old data
final Data oldData = operations.findAndModify(findById, update,
FindAndModifyOptions.options().upsert(true).returnNew(false), Data.class);
// Copy or create new instance
final Data newData;
if (oldData == null) {
newData = new Data(id);
}
else {
newData = new Data(oldData);
}
// Apply the same update
newData.setItems(newItems);
newData.incVersion();
return new Pair<Data>(oldData, newData);
}
可以工作,但不太漂亮,因为我们必须在旧实例的副本上重做我们已经在
Update
中做过的事情。
我们还考虑过首先加载旧实例并运行更新,但这并不安全,因为实体可能在加载和更新之间已被修改。这可以通过版本和乐观锁定来解决,但这使事情变得更加复杂。
不,无法使用 findAndModify 同时返回旧值和新值。
但是如果您想比较更新前后的实体状态,请执行以下步骤
下面的示例 sudo 代码将返回两个值
public Pair<Data> saveItems(String id, List<Item> items) {
final Query findById = ...;
final Update update = new Update();
// This is what we actually update
update.set(ENTITY_FIELD_ITEMS, newItems);
update.inc(ENTITY_FIELD_VERSION, 1);
// Try updating and return the old data
final Data oldData = findById(); //query to retrieve existing data
final Data newData = operations.findAndModify(findById, update,
FindAndModifyOptions.options().upsert(true).returnNew(true), Data.class);
return new Pair<Data>(oldData, newData);
}
这可能不是一个很好的方法,但我正在考虑在我的 findAndModify 更新中更新聚合管道。这将使我能够以
$$ROOT
的形式访问当前文档,然后我可以将其存储在同一文档的字段中,然后更新文档中的其他字段。
类似这样的事情
db.test.findAndModify({
query: {
_id: ObjectId('677bd298948f20de2040b43f')
},
update: [
{
$set: {
"previousState": "$$ROOT",
"key1": "updatedValue1",
"key2": "updatedValue2"
}
}
],
new: true
});
之前
{
"_id": {
"$oid": "677bd298948f20de2040b43f"
},
"key1": "value1",
"key2": "value2"
}
之后
{
"_id": {
"$oid": "677bd298948f20de2040b43f"
},
"key1": "updatedValue1",
"key2": "updatedValue2",
"previousState": {
"_id": {
"$oid": "677bd298948f20de2040b43f"
},
"key1": "value1",
"key2": "value2"
}
}
保留
new: true
将返回修改后的文档,并且 previousState
字段将具有已更新文档的旧状态。
这肯定会产生负面影响:
存储影响:如果文档很大,那么这可能很快会导致存储问题,因为您实际上将文档存储了两次。
性能影响:将文档存储在字段中的额外步骤也会对性能产生负面影响。
所以,需要谨慎使用。我认为小文档尺寸就可以了。