【问题标题】:Returning both old and new entities from Spring/MongoDB findAndModify从 Spring/MongoDB findAndModify 返回新旧实体
【发布时间】:2016-09-13 12:59:29
【问题描述】:

我们通过 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


    【解决方案1】:

    有没有办法从 findAndModify 返回新旧实体?

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

    【讨论】:

    • 是的,看来没有别的办法了。
    【解决方案2】:

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

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

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

    下面的示例 sudocode 将返回两个值

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

    【讨论】:

    • 第一个find 和后面的findAndModify 之间可能有另一个更新,所以oldData 可能已经过时了。
    • findAndModify 是线程安全的,所以它不会发生,因为两个步骤一个接一个地发生,你的用例就足够了。
    • 线程安全与此无关。可能有多个线程并行处理数据。所以即使findAndModify 是原子的,findfindAndModify 的序列也不是。接下来,可以从不同的进程甚至机器访问数据库。无法保证findfindAndUpdate 之间不会发生更新操作。
    • 这将创建一个具有多个写入器的竞争条件。至少我想你会想要在 findAndModify 之后执行 find ,这样你就可以拥有实际上一致修改的文档状态。这将允许您存档它或任何您想用它做的事情。那么问题是客户端是否会容忍返回状态的不一致。如果它像一个 REST API 或其他什么......事情可能会在写入后最终一致读取。对于更敏感的应用程序,您需要设计一个外部悲观锁定策略。
    猜你喜欢
    • 2015-11-18
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 2020-05-06
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多