从 3.6 版开始,MongoDB 支持会话(Session)的概念。会话的使用实现了 MongoDB 的因果一致性模型,它保证了以因果关系的顺序来运行操作。这些会话被分成 ServerSession 实例和 ClientSession 实例。在本文中,当我们谈到会话时,我们指的是 ClientSession。
警告:客户端会话内的操作与会话外的操作不隔离。
MongoOperations 和 ReactiveMongoOperations 都提供了将 ClientSession 绑定到操作的网关方法。MongoCollection 和 MongoDatabase 使用会话代理对象来实现 MongoDB 的集合和数据库接口,因此您不需要在每个调用上添加会话。这意味着对 MongoCollection#find() 的调用被委托给 MongoCollection#find(ClientSession)。
注意:诸如(Reactive)MongoOperations#getCollection 之类的方法返回本机 MongoDB Java 驱动网关对象(例如:MongoCollection),这些对象本身为 ClientSession 提供了专用方法,这些方法不是会话代理的。在直接与 MongoCollection 或 MongoDatabase 交互时,您应该在需要时提供 ClientSession,而不是通过 MongoOperations 上的#execute 回调之一。
以下示例显示了会话的用法:
ClientSessionOptions sessionOptions = ClientSessionOptions.builder() .causallyConsistent(true) .build(); // 从服务器获取一个新的会话 ClientSession session = client.startSession(sessionOptions); template.withSession(() -> session) .execute(action -> { Query query = query(where("name").is("Durzo Blint")); // 像以前一样使用 MongoOperation方法,ClientSession 会被自动应用 Person durzo = action.findOne(query, Person.class); Person azoth = new Person("Kylar Stern"); azoth.setMaster(durzo); action.insert(azoth); return azoth; }); // 关闭会话 session.close();
注意,在处理 DBRef 实例(尤其是延迟加载的实例)时,在加载所有数据之前不要关闭客户端会话,这一点至关重要。否则,延迟提取将失败。
响应式使用与命令式相同的构建块,如下例所示:
ClientSessionOptions sessionOptions = ClientSessionOptions.builder() .causallyConsistent(true) .build(); // 获取一个 Publisher,用于新的会话检索 Publisher<ClientSession> session = client.startSession(sessionOptions); template.withSession(session) .execute(action -> { Query query = query(where("name").is("Durzo Blint")); return action.findOne(query, Person.class) .flatMap(durzo -> { Person azoth = new Person("Kylar Stern"); azoth.setMaster(durzo); // 像以前一样使用 ReactiveMongoOperation 方法,自动获得并应用 ClientSession return action.insert(azoth); }); }, ClientSession::close) // 请确保关闭 ClientSession .subscribe(); // 在你订阅之前什么都不会发生
通过使用一个提供实际会话的 Publisher,你可以将会话的获取推迟到实际订阅的时候。但是,你仍然需要在完成后关闭会话,这样就不会用无效的会话污染服务器。当你不再需要会话时,使用 execution 上的 doFinally 钩子来调用 ClientSession#close() 方法关闭会话。如果你喜欢对会话本身有更多的控制,你可以通过驱动获得 ClientSession 对象,并通过供应商来提供它。