我正在使用这个Angular+Java+Spring Boot+MongoDB应用程序.最近,它得到了大量的操作(阅读:代码修改),但数据访问类基本上没有受到影响

判断mongod.log这是我在save()工作时看到的:

2018-04-11T15:04:06.840+0200 I COMMAND  [conn6] command pdfviewer.bookData command: find { find: "bookData", filter: { _id: "ID_1" }, limit: 1, singleBatch: true } planSummary: IDHACK keysExamined:1 docsExamined:1 idhack:1 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:1 nreturned:1 reslen:716 locks:{ Global: { acquireCount: { r: 4 } }, Database: { acquireCount: { r: 2 } }, Collection: { acquireCount: { r: 2 } } } protocol:op_query 102ms
2018-04-11T17:30:19.615+0200 I WRITE    [conn7] update pdfviewer.bookData query: { _id: "ID_1" } update: { _class: "model.BookData", _id: "ID_1", config: { mode: "normal", offlineEnabled: true }, metadata: { title: "PDFdePrueba3pag   copia  6 ", ...}, downloaded: false, currentPageNumber: 2, availablePages: 3, bookmarks: [], stats: { _id: "c919e517-3c68-462c-8396-d4ba391762e6", dateOpen: new Date(1523460575872), dateClose: new Date(1523460575951), timeZone: "+2", ... }, ... } keysExamined:1 docsExamined:1 nMatched:1 nModified:1 keyUpdates:0 writeConflicts:1 numYields:1 locks:{ Global: { acquireCount: { r: 2, w: 2 } }, Database: { acquireCount: { w: 2 } }, Collection: { acquireCount: { w: 2 } } } 315ms
2018-04-11T17:30:19.615+0200 I COMMAND  [conn7] command pdfviewer.$cmd command: update { update: "bookData", ordered: false, updates: [ { q: { _id: "ID_1" }, u: { _class: "model.BookData", _id: "ID_1", config: { mode: "normal", offlineEnabled: true }, metadata: { title: "PDFdePrueba3pag   copia  6 ", ...}, downloaded: false, currentPageNumber: 2, availablePages: 3, bookmarks: [], stats: { _id: "c919e517-3c68-462c-8396-d4ba391762e6", dateOpen: new Date(1523460575872), dateClose: new Date(1523460575951), timeZone: "+2", ... }, ... }, upsert: true } ] } keyUpdates:0 writeConflicts:0 numYields:0 reslen:55 locks:{ Global: { acquireCount: { r: 2, w: 2 } }, Database: { acquireCount: { w: 2 } }, Collection: { acquireCount: { w: 2 } } } protocol:op_query 316ms

这就是当它不存在时我看到的:

2018-04-11T18:13:21.864+0200 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:64271 #1 (1 connection now open)
2018-04-11T18:18:51.425+0200 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:64329 #2 (2 connections now open)
2018-04-11T18:19:06.967+0200 I NETWORK  [initandlisten] connection accepted from 127.0.0.1:64346 #3 (3 connections now open)

通过在调试过程中对日志(log)文件执行tail -f1,我看到这些连接在我的代码调用findById()save()时正确显示,因此应用程序似乎可以到达数据库.

这是(或多或少)相关的Java代码:

/* BookData.java */
@Document
public class BookData {

    @Id private String id;
    // Some more non-Id Strings...
    private Config config;
    private Metadata metadata;
    private Boolean downloaded;
    private Integer currentPageNumber;
    private int availablePages;
    private List<Bookmark> bookmarks;
    private StatsModel stats;

    @Transient private byte[] contents;

    public BookData() {}

    // getters and setters
}

/* BookDataRepository.java */
// MongoRepository comes from spring-boot-starter-parent-1.4.5.RELEASE
public interface BookDataRepository extends MongoRepository<BookData, String> {
    BookData findById(String id);
}

/* BookDataServiceImpl.java */
public BookData updateBookData(String id, BookData newData) {
    final BookData original = bookDataRepository.findById(id);
    if (original == null) {
        return null;
    }
    original.setCurrentPageNumber(Optional.ofNullable(newData.getCurrentPageNumber()).orElseGet(original::getCurrentPageNumber));
    // similar code for a couple other fields

    return bookDataRepository.save(original);
}

在调试过程中,我已经对这一部分进行了100次判断,似乎一切都正常:

  • findById(id)正确返回预期的BookData original对象:判断✓
  • newData包含用于更新的预期值:判断✓
  • 在调用save(original)之前,original已使用newData个值进行了正确修改:判断✓
  • save()执行无错误:判断✓
  • save()返回一个新的BookData,其值已正确更新:令我惊讶的是,判断✓
  • 返回save()次后,Mongo Shell中的db.bookData.find()查询显示值已更新:fail.
  • 返回save()后,通过对findById()的新调用检索到的BookData对象包含更新的值:fail(有时包含,有时不包含).

看起来MongoDB正在等待某种类型的flush(),但这不是一个JPA存储库,人们可以在这里调用saveAndFlush().

知道为什么会这样吗?

编辑:版本(根据要求):

  • Java 8
  • 弹簧靴1.4.5
  • MongoDB 3.2.6
  • Windows 10

我还包括BookData以上.

推荐答案

问题解决了

两个更新操作在保存前都调用了findById.问题是他们同时这么做,所以他们得到了相同的原始值

Once I added systemLog.verbosity: 3 to MongoDB's config.cfg so it logged all operations, it was clear that 2 different WRITE operations were happening at the same time (~500 ms apart) but using different values.
Then it was just a matter of moving the findById closer to the save and ensuring that the JS calls were done in order (by making one of the promises depend on the other).

事后看来,如果我使用MongoOperationsMongoTemplate,可能不会发生这种情况,它们提供了单一的updatefindAndModify方法,也允许单字段操作,而不是MongoRepository,在MongoRepository中,我被迫分3步(find,修改返回的实体,save)完成操作,并处理完整的文档.


编辑:我真的不喜欢我的第一个"让findById更接近save"的方法,所以最后我做了我觉得正确的和implemented custom save methods that used 102's finer-grained 103 API的.最终代码:

/* MongoRepository provides entity-based default Spring Data methods */
/* BookDataRepositoryCustom provides field-level update methods */
public interface BookDataRepository extends MongoRepository<BookData, String>, BookDataRepositoryCustom {

    BookData findById(String id);

}

/* Interface for the custom methods */
public interface BookDataRepositoryCustom {

    int saveCurrentPage(String id, Integer currentPage);
}

/* Custom implementation using MongoTemplate. */
@SuppressWarnings("unused")
public class BookDataRepositoryImpl implements BookDataRepositoryCustom {
    @Inject
    MongoTemplate mongoTemplate;

    @Override
    public int saveCurrentPage(String id, Integer currentPage) {
        Query query = new Query(Criteria.where("_id").is(id));
        Update update = new Update();
        update.set("currentPage", currentPage);

        WriteResult result = mongoTemplate.updateFirst(query, update, BookData.class);

        return result == null ? 0 : result.getN();
    }
}

// Old code: get entity from DB, update, save. 3 steps with plenty of room for interferences.
//        BookData bookData = bookDataRepository.findById(bookDataId);
//        bookData.setCurrentPage(currentPage);
//        bookDataRepository.save(bookData);
// New code: update single field. 1 step, 0 problems.
        bookDataRepository.saveCurrentPage(bookDataId, currentPage);

通过这样做,每个端点可以通过MongoTemplate根据需要频繁地执行update次,而不必担心覆盖不相关的字段,而且我仍然保留基于实体的MongoRepository个方法,用于创建新实体、findBy个方法、带注释的@Query等.

Mongodb相关问答推荐

通过mongoDB中的查找从管道中删除被阻止的用户

数组字段包含其他字段的数组值

Mongo DB-如果一个特定字段有多个文档匹配,则更新文档字段

MongoDB MQL,将列表一分为二,仅获取唯一值

我可以在 MongoDB 中将字段值设置为对象键吗?

Mongodb,在一个查询中用正则表达式更新部分字符串

DB.collection('comments').find() 不工作

Mongodb聚合查找连接两个对象字段的集合数组匹配对象索引字段的总和

在 mongodb 聚合中的阶段之后输出具有序列数字 id 的新集合

如何在 MongoDB 中存储时间?作为字符串?给出任意年/月/日?

使用 EventSourcing(NodeJS、MongoDB、JSON)跨多个偶尔连接的客户端同步数据

将 mongoose 字符串模式类型默认值设为空白并使该字段可选

NodeJS中的密码重置

$elemMatch 的 MongoDB 索引

RoR3 上的 Mongoid:1)如何在查询中返回特定字段? 2)需要什么 inverse_of ?

REACT 获取发布请求

$orderby 和 Sort 之间的 MongoDB 区别

为什么我新创建的 mongodb 本地数据库增长到 24GB?

多次使用位置 `$` 运算符来更新嵌套数组

我可以使用字符串作为 mongodb 文档的 ID 类型吗?