原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程八(内容来源:Spring中国教育管理中心)
11.13.索引和馆藏管理
MongoTemplate提供了一些管理索引和集合的方法。这些方法被收集到一个名为IndexOperations. 您可以通过调用该indexOps方法并传入集合名称或java.lang.Class实体的来访问这些操作(集合名称派生自.class,通过名称或注释元数据)。
以下清单显示了IndexOperations界面:
代码语言:javascript复制public interface IndexOperations {
void ensureIndex(IndexDefinition indexDefinition);
void dropIndex(String name);
void dropAllIndexes();
void resetIndexCache();
List<IndexInfo> getIndexInfo();
}
11.13.1.创建索引的方法
您可以使用 MongoTemplate 类在集合上创建索引以提高查询性能,如以下示例所示:
代码语言:javascript复制mongoTemplate.indexOps(Person.class).ensureIndex(new Index().on("name",Order.ASCENDING));
ensureIndex 确保为集合存在提供的 IndexDefinition 的索引。
您可以使用IndexDefinition,GeoSpatialIndex和TextIndexDefinition类创建标准、地理空间和文本索引。例如,给定Venue在上一节中定义的类,您可以声明一个地理空间查询,如以下示例所示:
代码语言:javascript复制mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));
Index并GeospatialIndex支持排序规则的配置。
11.13.2.访问索引信息
该IndexOperations接口具有getIndexInfo返回IndexInfo对象列表的方法。此列表包含在集合上定义的所有索引。以下示例在Person具有age属性的类上定义索引:
代码语言:javascript复制template.indexOps(Person.class).ensureIndex(new Index().on("age", Order.DESCENDING).unique());
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
// Contains
// [IndexInfo [fieldSpec={_id=ASCENDING}, name=_id_, unique=false, sparse=false],
// IndexInfo [fieldSpec={age=DESCENDING}, name=age_-1, unique=true, sparse=false]]
11.13.3.处理集合的方法
以下示例显示了如何创建集合:
示例 106. 使用集合处理 MongoTemplate
代码语言:javascript复制MongoCollection<Document> collection = null;
if (!mongoTemplate.getCollectionNames().contains("MyNewCollection")) {
collection = mongoTemplate.createCollection("MyNewCollection");
}
mongoTemplate.dropCollection("MyNewCollection");
- getCollectionNames:返回一组集合名称。
- collectionExists:检查是否存在具有给定名称的集合。
- createCollection:创建一个无上限的集合。
- dropCollection:删除集合。
- getCollection:按名称获取集合,如果它不存在则创建它。
集合创建允许自定义CollectionOptions并支持排序规则。
11.14.运行命令
您可以MongoDatabase.runCommand( )使用 上的executeCommand(…)方法获取 MongoDB 驱动程序的方法MongoTemplate。这些方法还将异常转换为 Spring 的DataAccessException层次结构。
11.14.1.运行命令的方法
- Document executeCommand (Document command):运行 MongoDB 命令。
- Document executeCommand (Document command, ReadPreference readPreference):使用给定的可为空的 MongoDB 运行 MongoDB 命令ReadPreference。
- Document executeCommand (String jsonCommand):运行以 JSON 字符串表示的 MongoDB 命令。
11.15.生命周期事件
MongoDB 映射框架包括多个 org.springframework.context.ApplicationEvent事件,您的应用程序可以通过在ApplicationContext. 由于基于 Spring 的ApplicationContext事件基础设施,其他产品(例如 Spring Integration)能够轻松接收这些事件,因为它们是基于 Spring 的应用程序中众所周知的事件机制。
要在对象通过转换过程(将域对象转换为org.bson.Document)之前拦截对象,您可以注册一个 AbstractMongoEventListener覆盖该onBeforeConvert方法的子类。当事件被调度时,你的监听器被调用并在它进入转换器之前传递域对象。以下示例显示了如何执行此操作:
代码语言:javascript复制public class BeforeConvertListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeConvert(BeforeConvertEvent<Person> event) {
... does some auditing manipulation, set timestamps, whatever ...
}
}
要在对象进入数据库之前对其进行拦截,您可以注册一个 org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener覆盖该onBeforeSave方法的子类。当事件被调度时,你的监听器被调用并传递域对象和转换后的com.mongodb.Document. 以下示例显示了如何执行此操作:
代码语言:javascript复制public class BeforeSaveListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeSave(BeforeSaveEvent<Person> event) {
… change values, delete them, whatever …
}
}
在 Spring ApplicationContext 中声明这些 bean 会导致在调度事件时调用它们。
以下回调方法存在于 AbstractMappingEventListener:
- onBeforeConvert:调用在MongoTemplate insert,insertList和save操作之前将对象转换为一个Document由一个MongoConverter。
- onBeforeSave:在调用MongoTemplate insert,insertList以及save操作之前插入或保存Document在数据库中。
- onAfterSave:在调用MongoTemplate insert,insertList以及save操作后插入或保存Document在数据库中。
- onAfterLoad:在叫MongoTemplate find,findAndRemove,findOne,和getCollection之后的那些方法Document已经从数据库中检索。
- onAfterConvert:在调用MongoTemplate find,findAndRemove,findOne,和getCollection在后的方法Document已被从数据库中检索被转化为一个POJO。
仅针对根级别类型发出生命周期事件。在文档根中用作属性的复杂类型不受事件发布的约束,除非它们是用 注释的文档引用@DBRef。
生命周期事件取决于 ApplicationEventMulticaster,在这种情况下SimpleApplicationEventMulticaster可以使用 配置TaskExecutor,因此不保证处理事件时。
11.16.实体回调
Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。那些所谓的EntityCallback实例提供了一种方便的方法来检查和潜在地以回调风格修改实体。 AnEntityCallback看起来很像一个专门的ApplicationListener. 一些 Spring Data 模块发布BeforeSaveEvent允许修改给定实体的存储特定事件(例如)。在某些情况下,例如使用不可变类型时,这些事件可能会导致麻烦。此外,事件发布依赖于 ApplicationEventMulticaster. 如果使用异步配置TaskExecutor它可能会导致不可预测的结果,因为事件处理可以分叉到线程上。
实体回调为同步 API 和反应式 API 提供集成点,以保证在处理链中定义明确的检查点按顺序执行,返回潜在修改的实体或反应式包装器类型。
实体回调通常按 API 类型分隔。这种分离意味着同步 API 仅考虑同步实体回调,而反应式实现仅考虑反应式实体回调。
Spring Data Commons 2.2 引入了实体回调 API。这是应用实体修改的推荐方式。在调用可能已注册的实例之前,ApplicationEvents仍会发布特定于现有商店的信息。EntityCallback
11.16.1.实现实体回调
AnEntityCallback通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常带有一组EntityCallback涵盖实体生命周期的预定义接口。
例 107. 解剖 EntityCallback
代码语言:javascript复制@FunctionalInterface
public interface BeforeSaveCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked before a domain object is saved.
* Can return either the same or a modified instance.
*
* @return the domain object to be persisted.
*/
T onBeforeSave(T entity <2>, String collection <3>);
}
BeforeSaveCallback在保存实体之前要调用的特定方法。返回一个可能被修改的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久化到的集合。
例 108. 反应式的剖析 EntityCallback
代码语言:javascript复制@FunctionalInterface
public interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked on subscription, before a domain object is saved.
* The returned Publisher can emit either the same or a modified instance.
*
* @return Publisher emitting the domain object to be persisted.
*/
Publisher<T> onBeforeSave(T entity <2>, String collection <3>);
}
BeforeSaveCallback在保存实体之前,在订阅时调用的特定方法。发出一个可能被修改的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久化到的集合。
可选的实体回调参数由实现 Spring Data 模块定义并从EntityCallback.callback().
实现适合您的应用程序需求的接口,如下例所示:
示例 109. 示例 BeforeSaveCallback
代码语言:javascript复制class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered {
@Override
public Object onBeforeSave(Person entity, String collection) {
if(collection == "user") {
return // ...
}
return // ...
}
@Override
public int getOrder() {
return 100;
}
}
根据您的要求实现回调。
如果存在多个相同域类型的实体回调,则可能对实体回调进行排序。排序遵循最低优先级。
11.16.2.注册实体回调
EntityCallback如果 bean 在ApplicationContext. 大多数模板 API 已经实现ApplicationContextAware,因此可以访问ApplicationContext
以下示例解释了一组有效的实体回调注册:
示例 110. EntityCallbackBean 注册示例
代码语言:javascript复制@Order(1)
@Component
class First implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person person) {
return // ...
}
}
@Component
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
Ordered {
@Override
public Object onBeforeSave(Person entity, String collection) {
// ...
}
@Override
public int getOrder() {
return 100;
}
}
@Configuration
public class EntityCallbackConfiguration {
@Bean
BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() {
return (BeforeSaveCallback<Person>) it -> // ...
}
}
@Component
class UserCallbacks implements BeforeConvertCallback<User>,
BeforeSaveCallback<User> {
@Override
public Person onBeforeConvert(User user) {
return // ...
}
@Override
public Person onBeforeSave(User user) {
return // ...
}
}
BeforeSaveCallback从@Order注释中接收其命令。
BeforeSaveCallback通过Ordered接口实现接收其订单。
BeforeSaveCallback使用 lambda 表达式。默认情况下无序并最后调用。请注意,由 lambda 表达式实现的回调不会公开类型信息,因此使用不可分配的实体调用这些会影响回调吞吐量。使用classorenum为回调 bean 启用类型过滤。
在单个实现类中组合多个实体回调接口。
11.16.3.存储特定的 EntityCallbacks
Spring Data MongoDB 使用EntityCallbackAPI 作为其审计支持并对以下回调做出反应。
11.17.异常翻译
Spring 框架为各种数据库和映射技术提供异常转换。这在传统上用于 JDBC 和 JPA。Spring 对 MongoDB 的支持通过提供 org.springframework.dao.support.PersistenceExceptionTranslator接口的实现将此功能扩展到 MongoDB 数据库。
映射到 Spring一致的数据访问异常层次结构背后的动机是,您可以编写可移植和描述性的异常处理代码,而无需针对 MongoDB 错误代码进行编码。Spring 的所有数据访问异常都继承自根DataAccessException类,因此您可以确保在单个 try-catch 块中捕获所有与数据库相关的异常。请注意,并非 MongoDB 驱动程序抛出的所有异常都继承自MongoException该类。保留内部异常和消息,因此不会丢失任何信息。
由 MongoExceptionTranslatorarecom.mongodb.Network to DataAccessResourceFailureException和MongoException错误代码 1003、12001、12010、12011 和 12012执行的一些映射到InvalidDataAccessApiUsageException. 查看实现以获取有关映射的更多详细信息。
11.18.执行回调
所有 Spring 模板类的一个共同设计特性是所有功能都路由到模板的execute回调方法之一中。这样做有助于确保始终如一地执行可能需要的异常和任何资源管理。虽然 JDBC 和 JMS 比 MongoDB 更需要这个特性,但它仍然提供了一个单一的地方来进行异常转换和日志记录。因此,使用这些execute回调是访问 MongoDB 驱动程序MongoDatabase和MongoCollection对象以执行未作为方法公开的罕见操作的首选方式MongoTemplate。
以下列表描述了execute回调方法。
- <T> T execute (Class<?> entityClass, CollectionCallback<T> action):CollectionCallback为指定类的实体集合运行给定。
- <T> T execute (String collectionName, CollectionCallback<T> action):在给CollectionCallback定名称的集合上运行给定。
- <T> T execute (DbCallback<T> action):运行 DbCallback,根据需要翻译任何异常。Spring Data MongoDB 为 2.2 版中引入到 MongoDB 的聚合框架提供支持。
- <T> T execute (String collectionName, DbCallback<T> action):DbCallback在给定名称的集合上运行 a ,根据需要翻译任何异常。
- <T> T executeInSession (DbCallback<T> action):DbCallback在与数据库的同一连接内运行给定的,以确保在写入繁重的环境中的一致性,您可以在该环境中读取您写入的数据。
以下示例使用CollectionCallback返回有关索引的信息:
代码语言:javascript复制boolean hasIndex = template.execute("geolocation", new CollectionCallbackBoolean>() {
public Boolean doInCollection(Venue.class, DBCollection collection) throws MongoException, DataAccessException {
List<Document> indexes = collection.getIndexInfo();
for (Document document : indexes) {
if ("location_2d".equals(document.get("name"))) {
return true;
}
}
return false;
}
});
11.19.GridFS 支持
MongoDB 支持在其文件系统 GridFS 中存储二进制文件。Spring Data MongoDB 提供了一个GridFsOperations接口以及相应的实现,GridFsTemplate让您与文件系统进行交互。您可以GridFsTemplate通过传递 aMongoDatabaseFactory和a 来设置实例,MongoConverter如下例所示:
示例 111. GridFsTemplate 的 JavaConfig 设置
代码语言:javascript复制class GridFsConfiguration extends AbstractMongoClientConfiguration {
// … further configuration omitted
@Bean
public GridFsTemplate gridFsTemplate() {
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
}
对应的XML配置如下:
示例 112. GridFsTemplate 的 XML 配置
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo
https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<mongo:db-factory id="mongoDbFactory" dbname="database" />
<mongo:mapping-converter id="converter" />
<bean class="org.springframework.data.mongodb.gridfs.GridFsTemplate">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg ref="converter" />
</bean>
</beans>
现在可以注入模板并用于执行存储和检索操作,如以下示例所示:
示例 113.使用 GridFsTemplate 存储文件
代码语言:javascript复制class GridFsClient {
@Autowired
GridFsOperations operations;
@Test
public void storeFileToGridFs() {
FileMetadata metadata = new FileMetadata();
// populate metadata
Resource file = … // lookup File or Resource
operations.store(file.getInputStream(), "filename.txt", metadata);
}
}
这些store(…)操作需要一个InputStream、一个文件名和(可选的)关于要存储的文件的元数据信息。元数据可以是任意对象,它将被MongoConverter配置为GridFsTemplate. 或者,您也可以提供一个Document.
您可以通过find(…)或getResources(…)方法从文件系统读取文件。我们先来看看find(…)方法。您可以找到单个文件或多个与Query. 您可以使用GridFsCriteriahelper 类来定义查询。它提供了静态工厂方法来封装默认元数据字段(例如whereFilename()和whereContentType())或自定义元数据字段到whereMetaData()。下面的例子展示了如何使用GridFsTemplate来查询文件:
示例 114.使用 GridFsTemplate 查询文件
代码语言:javascript复制class GridFsClient {
@Autowired
GridFsOperations operations;
@Test
public void findFilesInGridFs() {
GridFSFindIterable result = operations.find(query(whereFilename().is("filename.txt")))
}
}
目前,MongoDB 不支持在从 GridFS 检索文件时定义排序条件。出于这个原因,在Query传递给find(…)方法的实例上定义的任何排序标准都将被忽略。
从 GridFs 读取文件的另一个选项是使用ResourcePatternResolver接口引入的方法。它们允许将 Ant 路径传递到方法中,从而可以检索与给定模式匹配的文件。下面的例子展示了如何使用GridFsTemplate读取文件:
示例 115.使用 GridFsTemplate 读取文件
代码语言:javascript复制class GridFsClient {
@Autowired
GridFsOperations operations;
@Test
public void readFilesFromGridFs() {
GridFsResources[] txtFiles = operations.getResources("*.txt");
}
}
GridFsOperations扩展ResourcePatternResolver并让GridFsTemplate(例如)插入到ApplicationContextMongoDB 数据库中读取 Spring 配置文件。
11.20.带有可尾游标的无限流
默认情况下,当客户端耗尽游标提供的所有结果时,MongoDB 会自动关闭游标。在耗尽时关闭游标会将流变成有限流。对于有上限的集合,您可以使用在客户端消耗所有最初返回的数据后保持打开状态的Tailable Cursor。
可以使用 MongoOperations.createCollection. 为此,请提供所需的CollectionOptions.empty().capped()….
Tailable 游标可以与命令式和反应式 MongoDB API 一起使用。强烈建议使用反应式变体,因为它占用的资源较少。但是,如果您不能使用反应式 API,您仍然可以使用 Spring 生态系统中已经流行的消息传递概念。
11.20.1。Tailable Cursors withMessageListener
使用同步驱动程序侦听上限集合会创建一个长时间运行的阻塞任务,该任务需要委托给单独的组件。在这种情况下,我们需要首先创建一个MessageListenerContainer,这将是运行特定SubscriptionRequest. 春季数据的MongoDB已经附带了操作上的默认实现MongoTemplate,并能创建和运行的Task一个实例TailableCursorRequest。
以下示例显示了如何将可尾游标与MessageListener实例一起使用:
示例 116.带有MessageListener实例的可尾游标
代码语言:javascript复制MessageListenerContainer container = new DefaultMessageListenerContainer(template);
container.start();
MessageListener<Document, User> listener = System.out::println;
TailableCursorRequest request = TailableCursorRequest.builder()
.collection("orders")
.filter(query(where("value").lt(100)))
.publishTo(listener)
.build();
container.register(request, User.class);
// ...
container.stop();
启动容器会初始化资源并Task为已注册的SubscriptionRequest实例启动实例。启动后添加的请求会立即运行。
定义在Message收到a 时调用的侦听器。将Message#getBody()转换为请求的域类型。使用Document接收的原始效果,无需转换。
设置要收听的集合。
为要接收的文档提供可选过滤器。
设置消息侦听器以将传入的Messages发布到。
注册请求。返回的Subscription可用于检查当前Task状态并取消它以释放资源。
一旦确定不再需要容器,请不要忘记停止容器。这样做会停止Task容器内所有正在运行的实例。
11.20.2.反应式可尾游标
使用具有反应数据类型的可尾游标允许构建无限流。可尾游标保持打开状态,直到它在外部关闭。当新文档到达上限集合时,它会发出数据。
如果查询返回不匹配或游标返回集合“结尾”处的文档,然后应用程序删除该文档,则可跟踪游标可能会失效或无效。以下示例显示了如何创建和使用无限流查询:
示例 117. 使用 ReactiveMongoOperations 的无限流查询
代码语言:javascript复制Flux<Person> stream = template.tail(query(where("name").is("Joe")), Person.class);
Disposable subscription = stream.doOnNext(person -> System.out.println(person)).subscribe();
// …
// Later: Dispose the subscription to close the stream
subscription.dispose();
Spring Data MongoDB Reactive 存储库通过使用@Tailable. 这适用于返回的方法Flux和能够发出多个元素的其他反应类型,如以下示例所示:
示例 118. 使用 ReactiveMongoRepository 进行无限流查询
代码语言:javascript复制public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
@Tailable
Flux<Person> findByFirstname(String firstname);
}
Flux<Person> stream = repository.findByFirstname("Joe");
Disposable subscription = stream.doOnNext(System.out::println).subscribe();
// …
// Later: Dispose the subscription to close the stream
subscription.dispose();
11.21.更改流
从 MongoDB 3.6 开始,Change Streams允许应用程序获得有关更改的通知,而无需跟踪 oplog。
Change Stream 支持仅适用于副本集或分片集群。
Change Streams 可以与命令式和反应式 MongoDB Java 驱动程序一起使用。强烈建议使用反应式变体,因为它占用的资源较少。但是,如果您无法使用响应式 API,您仍然可以通过使用 Spring 生态系统中已经流行的消息传递概念来获取更改事件。
可以同时在集合和数据库级别进行观察,而数据库级别变体发布来自数据库内所有集合的更改。订阅数据库更改流时,请确保为事件类型使用合适的类型,因为转换可能无法正确应用于不同的实体类型。有疑问,请使用Document.
11.21.1.更改流MessageListener
使用同步驱动程序侦听更改流会创建一个长时间运行的阻塞任务,该任务需要委派给单独的组件。在这种情况下,我们需要首先创建一个MessageListenerContainer,它将是运行特定SubscriptionRequest任务的主要入口点。春季数据的MongoDB已经附带了操作上的默认实现MongoTemplate,并能创建和运行的Task一个实例ChangeStreamRequest。
以下示例显示了如何将 Change Streams 与MessageListener实例一起使用:
示例 119. 使用MessageListener实例更改流
代码语言:javascript复制MessageListenerContainer container = new DefaultMessageListenerContainer(template);
container.start();
MessageListener<ChangeStreamDocument<Document>, User> listener = System.out::println;
ChangeStreamRequestOptions options = new ChangeStreamRequestOptions("user", ChangeStreamOptions.empty());
Subscription subscription = container.register(new ChangeStreamRequest<>(listener, options), User.class);
// ...
container.stop();
启动容器会初始化资源并Task为已注册的SubscriptionRequest实例启动实例。启动后添加的请求会立即运行。
定义在Message收到a 时调用的侦听器。将Message#getBody()转换为请求的域类型。使用Document接收的原始效果,无需转换。
将集合设置为收听并通过 提供其他选项ChangeStreamOptions。
注册请求。返回的Subscription可用于检查当前Task状态并取消它以释放资源。
一旦确定不再需要容器,请不要忘记停止容器。这样做会停止Task容器内所有正在运行的实例。
处理时的错误会传递给 org.springframework.util.ErrorHandler. 如果没有另外说明ErrorHandler,默认情况下会应用日志附加。
请使用register(request, body, errorHandler)以提供附加功能。