从 Spring/MongoDB findAndModify 返回旧实体和新实体

问题描述 投票:0回答:3

我们通过 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
中做过的事情。

我们还考虑过首先加载旧实例并运行更新,但这并不安全,因为实体可能在加载和更新之间已被修改。这可以通过版本和乐观锁定来解决,但这使事情变得更加复杂。

spring mongodb mongodb-query spring-data mongodb-java
3个回答
5
投票

是否有某种方法可以从 findAndModify 返回旧实体和新实体?

,无法使用

findAndModify
同时返回旧值和新值。


0
投票

不,无法使用 findAndModify 同时返回旧值和新值。

但是如果您想比较更新前后的实体状态,请执行以下步骤

  1. 在更新之前使用主键检索数据并存储在 oldData 变量中
  2. 在 findAndModify 查询中将 returnNew 更改为 true 以将其保存在 newData 下

下面的示例 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);
    }

0
投票

这可能不是一个很好的方法,但我正在考虑在我的 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
字段将具有已更新文档的旧状态。

这肯定会产生负面影响:

  1. 存储影响:如果文档很大,那么这可能很快会导致存储问题,因为您实际上将文档存储了两次。

  2. 性能影响:将文档存储在字段中的额外步骤也会对性能产生负面影响。

所以,需要谨慎使用。我认为小文档尺寸就可以了。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.