Spring Data Lovelace for MongoDB 有哪些新功能?

工程 | Christoph Strobl | 2018 年 9 月 27 日 | ...

在过去的一年中,NoSQL 存储得到了许多改进,包括大量新功能和扩展功能。我们与 MongoDB 的驱动程序团队进行了紧密合作,因此该版本已经提供了对会话、变更流、模式验证和(当然)事务的良好支持。

最有趣的新功能可能是 MongoDB 4.0 对多文档事务的支持。如果您之前关注过此博客,您可能已经阅读了我们的实践指南,其中解释了 `ClientSessions`(这是主要构建块)和事务本身。简而言之,SpringData 提供了在项目中利用Spring 管理的事务支持所需的一切。要使用它,请在您的配置中声明 `MongoTransactionManager`,如下例所示:

@Configuration
class Config extends AbstractMongoConfiguration {

  @Bean
  MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
    return new MongoTransactionManager(dbFactory);
  }
}

事务在快照之上运行,因此,在事务期间所做的更改在 oplog 中会显得有些奇怪。幸运的是,MongoDB 3.6 中引入了变更流,以取代当前的 oplog 跟踪,提供了一个支持良好的解决方案,能够解开事务中的条目。变更流允许您在数据库甚至集合级别发生特定事件时收到通知。它通过使用聚合来过滤事件,同时也允许您在给定的检查点或时间戳恢复流。使用响应式 API 消费变更流是最自然的方式,如下例所示:

Flux changeStream = reactiveTemplate
  .changeStream(newAggregation(match(where("operationType").is("insert"))),
    Person.class, ChangeStreamOptions.empty(), "users");

前面的流仅返回插入到 `users` 集合中的新文档,并将它们映射到 `Person` 域类型。使用同步 API 实现相同的功能会创建一个需要委托给单独组件(`MessageListenerContainer`)的长时间运行的阻塞任务,如下例所示:

MessageListenerContainer container =
  new DefaultMessageListenerContainer(template);
container.start();

MessageListener<ChangeStreamDocument<Document>, Person> listener =
  System.out::println;

ChangeStreamRequest request = ChangeStreamRequest.builder()
  .collection("users")
  .filter(newAggregation(match(where("operationType").is("insert")))
  .publishTo(listener)
  .build();

container.register(request, Person.class);

// …

container.stop();

拥有一个 `MessageListenerContainer` 打开了其他可能性。到目前为止,使用具有封顶集合的 Tailable Cursor 的无限流仅限于响应式 API。现在,只需将 `SubscriptionRequest` 传递给容器即可,如下例所示:

MessageListener<Document, Person> listener = System.out::println;

TailableCursorRequest request = TailableCursorRequest.builder()
  .collection("users")
  .filter(query(where("active").eq(true)))
  .publishTo(listener)
  .build();

container.register(request, Person.class);

前面的代码片段监听 `users` 集合上的插入操作,并将 `Messages` 发布到控制台。相当长一段时间以来,MongoDB 一直允许使用遵循整体查询语法的验证器来验证添加到集合或在集合中更新的文档。凭借其新版本,MongoDB 通过添加对JSON Schema的支持扩展了此验证,该支持允许您以更面向对象的方式定义文档蓝图。

{
  "type": "object",
  "required": [ "lastname" ],
  "properties": {
    "lastname": {
      "type": "string"
    },
    "address": {
      "type": "object",
      "properties": {
        "postCode": { "type": "string", "minLength": 4, "maxLength": 5 }
      }
    }
  }
}

`MongoJsonSchema` 及其构建器是 Spring Data 定义集合模式的 API 网关。以下示例演示了如何使用它:

MongoJsonSchema jsonSchema = MongoJsonSchema.builder()
  .required("lastname")
  .properties(
     string("lastname"),
     object("address")
       .properties(string("postCode").minLength(4).maxLength(5))
  ).build();

CollectionOptions options = CollectionOptions.empty().schema(jsonSchema);

template.createCollection(Person.class, options);

尽管如此,MongoDB 作为一个无模式存储,允许字段在每个文档级别具有不同的类型。您还可以使用模式来查询匹配蓝图的文档,而无需强制对集合进行验证,如下例所示:

template.query(Person.class)
  .matching(query(matchingDocumentStructure(jsonSchema))).all();

另一个对 MongoDB 模块的微小但显着的增强是用于检索分配给单个字段的所有值的唯一列表的 DISTINCT 值查询。如前所述,字段不必具有相同的数据类型。这就是为什么默认情况下,`distinct` 返回一个未类型化的 `Collection`。以下示例演示了如何使用 `distinct`:

List<Object> distinctValues = template.query(Person.class)
  .distinct("active")
  .all();

active 标志可能是 "y/n"、"true/false" 和 "0/1" 对的混合体,它们在存储本身中表示为 `String`、`boolean` 和(可能)`int32`。但是,在您确信至少属性类型的情况下,使用 `as` 投影获取强类型集合可能会很有用。以下示例使用 `as` 投影:

List<Boolean> distinctValues = template.query(Person.class)
  .distinct("active")
  .as(Boolean)
  .all();

MongoDB 模块中还有其他一些增强功能,但我们在这里没有足够的空间来详细介绍。请务必查看参考文档的新功能部分,以了解有关响应式 MapReduce、默认排序、`findAndReplace(…)` 方法以及新的聚合运算符和阶段的更多信息。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有