原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程八(内容来源:Spring中国教育管理中心)
MongoTemplate提供了一些管理索引和集合的方法。这些方法被收集到一个名为IndexOperations. 您可以通过调用该indexOps方法并传入集合名称或java.lang.Class实体的来访问这些操作(集合名称派生自.class,通过名称或注释元数据)。
以下清单显示了IndexOperations界面:
public interface IndexOperations {
void ensureIndex(IndexDefinition indexDefinition);
void dropIndex(String name);
void dropAllIndexes();
void resetIndexCache();
List<IndexInfo> getIndexInfo();
}
您可以使用 MongoTemplate 类在集合上创建索引以提高查询性能,如以下示例所示:
mongoTemplate.indexOps(Person.class).ensureIndex(new Index().on("name",Order.ASCENDING));
ensureIndex 确保为集合存在提供的 IndexDefinition 的索引。
您可以使用IndexDefinition,GeoSpatialIndex和TextIndexDefinition类创建标准、地理空间和文本索引。例如,给定Venue在上一节中定义的类,您可以声明一个地理空间查询,如以下示例所示:
mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location"));
Index并GeospatialIndex支持排序规则的配置。
该IndexOperations接口具有getIndexInfo返回IndexInfo对象列表的方法。此列表包含在集合上定义的所有索引。以下示例在Person具有age属性的类上定义索引:
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]]
以下示例显示了如何创建集合:
示例 106. 使用集合处理 MongoTemplate
MongoCollection<Document> collection = null;
if (!mongoTemplate.getCollectionNames().contains("MyNewCollection")) {
collection = mongoTemplate.createCollection("MyNewCollection");
}
mongoTemplate.dropCollection("MyNewCollection");
集合创建允许自定义CollectionOptions并支持排序规则。
您可以MongoDatabase.runCommand( )使用 上的executeCommand(…)方法获取 MongoDB 驱动程序的方法MongoTemplate。这些方法还将异常转换为 Spring 的DataAccessException层次结构。
MongoDB 映射框架包括多个 org.springframework.context.ApplicationEvent事件,您的应用程序可以通过在ApplicationContext. 由于基于 Spring 的ApplicationContext事件基础设施,其他产品(例如 Spring Integration)能够轻松接收这些事件,因为它们是基于 Spring 的应用程序中众所周知的事件机制。
要在对象通过转换过程(将域对象转换为org.bson.Document)之前拦截对象,您可以注册一个 AbstractMongoEventListener覆盖该onBeforeConvert方法的子类。当事件被调度时,你的监听器被调用并在它进入转换器之前传递域对象。以下示例显示了如何执行此操作:
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. 以下示例显示了如何执行此操作:
public class BeforeSaveListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeSave(BeforeSaveEvent<Person> event) {
… change values, delete them, whatever …
}
}
在 Spring ApplicationContext 中声明这些 bean 会导致在调度事件时调用它们。
以下回调方法存在于 AbstractMappingEventListener:
仅针对根级别类型发出生命周期事件。在文档根中用作属性的复杂类型不受事件发布的约束,除非它们是用 注释的文档引用@DBRef。
生命周期事件取决于 ApplicationEventMulticaster,在这种情况下SimpleApplicationEventMulticaster可以使用 配置TaskExecutor,因此不保证处理事件时。
Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。那些所谓的EntityCallback实例提供了一种方便的方法来检查和潜在地以回调风格修改实体。 AnEntityCallback看起来很像一个专门的ApplicationListener. 一些 Spring Data 模块发布BeforeSaveEvent允许修改给定实体的存储特定事件(例如)。在某些情况下,例如使用不可变类型时,这些事件可能会导致麻烦。此外,事件发布依赖于 ApplicationEventMulticaster. 如果使用异步配置TaskExecutor它可能会导致不可预测的结果,因为事件处理可以分叉到线程上。
实体回调为同步 API 和反应式 API 提供集成点,以保证在处理链中定义明确的检查点按顺序执行,返回潜在修改的实体或反应式包装器类型。
实体回调通常按 API 类型分隔。这种分离意味着同步 API 仅考虑同步实体回调,而反应式实现仅考虑反应式实体回调。
Spring Data Commons 2.2 引入了实体回调 API。这是应用实体修改的推荐方式。在调用可能已注册的实例之前,ApplicationEvents仍会发布特定于现有商店的信息。EntityCallback
AnEntityCallback通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常带有一组EntityCallback涵盖实体生命周期的预定义接口。
例 107. 解剖 EntityCallback
@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
@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
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;
}
}
根据您的要求实现回调。
如果存在多个相同域类型的实体回调,则可能对实体回调进行排序。排序遵循最低优先级。
EntityCallback如果 bean 在ApplicationContext. 大多数模板 API 已经实现ApplicationContextAware,因此可以访问ApplicationContext
以下示例解释了一组有效的实体回调注册:
示例 110. EntityCallbackBean 注册示例
@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 启用类型过滤。
在单个实现类中组合多个实体回调接口。
Spring Data MongoDB 使用EntityCallbackAPI 作为其审计支持并对以下回调做出反应。
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. 查看实现以获取有关映射的更多详细信息。
所有 Spring 模板类的一个共同设计特性是所有功能都路由到模板的execute回调方法之一中。这样做有助于确保始终如一地执行可能需要的异常和任何资源管理。虽然 JDBC 和 JMS 比 MongoDB 更需要这个特性,但它仍然提供了一个单一的地方来进行异常转换和日志记录。因此,使用这些execute回调是访问 MongoDB 驱动程序MongoDatabase和MongoCollection对象以执行未作为方法公开的罕见操作的首选方式MongoTemplate。
以下列表描述了execute回调方法。
以下示例使用CollectionCallback返回有关索引的信息:
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;
}
});
MongoDB 支持在其文件系统 GridFS 中存储二进制文件。Spring Data MongoDB 提供了一个GridFsOperations接口以及相应的实现,GridFsTemplate让您与文件系统进行交互。您可以GridFsTemplate通过传递 aMongoDatabaseFactory和a 来设置实例,MongoConverter如下例所示:
示例 111. GridFsTemplate 的 JavaConfig 设置
class GridFsConfiguration extends AbstractMongoClientConfiguration {
// … further configuration omitted
@Bean
public GridFsTemplate gridFsTemplate() {
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
}
对应的XML配置如下:
示例 112. GridFsTemplate 的 XML 配置
<?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 存储文件
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 查询文件
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 读取文件
class GridFsClient {
@Autowired
GridFsOperations operations;
@Test
public void readFilesFromGridFs() {
GridFsResources[] txtFiles = operations.getResources("*.txt");
}
}
GridFsOperations扩展ResourcePatternResolver并让GridFsTemplate(例如)插入到ApplicationContextMongoDB 数据库中读取 Spring 配置文件。
默认情况下,当客户端耗尽游标提供的所有结果时,MongoDB 会自动关闭游标。在耗尽时关闭游标会将流变成有限流。对于有上限的集合,您可以使用在客户端消耗所有最初返回的数据后保持打开状态的Tailable Cursor。
可以使用 MongoOperations.createCollection. 为此,请提供所需的CollectionOptions.empty().capped()….
Tailable 游标可以与命令式和反应式 MongoDB API 一起使用。强烈建议使用反应式变体,因为它占用的资源较少。但是,如果您不能使用反应式 API,您仍然可以使用 Spring 生态系统中已经流行的消息传递概念。
使用同步驱动程序侦听上限集合会创建一个长时间运行的阻塞任务,该任务需要委托给单独的组件。在这种情况下,我们需要首先创建一个MessageListenerContainer,这将是运行特定SubscriptionRequest. 春季数据的MongoDB已经附带了操作上的默认实现MongoTemplate,并能创建和运行的Task一个实例TailableCursorRequest。
以下示例显示了如何将可尾游标与MessageListener实例一起使用:
示例 116.带有MessageListener实例的可尾游标
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容器内所有正在运行的实例。
使用具有反应数据类型的可尾游标允许构建无限流。可尾游标保持打开状态,直到它在外部关闭。当新文档到达上限集合时,它会发出数据。
如果查询返回不匹配或游标返回集合“结尾”处的文档,然后应用程序删除该文档,则可跟踪游标可能会失效或无效。以下示例显示了如何创建和使用无限流查询:
示例 117. 使用 ReactiveMongoOperations 的无限流查询
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 进行无限流查询
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();
从 MongoDB 3.6 开始,Change Streams允许应用程序获得有关更改的通知,而无需跟踪 oplog。
Change Stream 支持仅适用于副本集或分片集群。
Change Streams 可以与命令式和反应式 MongoDB Java 驱动程序一起使用。强烈建议使用反应式变体,因为它占用的资源较少。但是,如果您无法使用响应式 API,您仍然可以通过使用 Spring 生态系统中已经流行的消息传递概念来获取更改事件。
可以同时在集合和数据库级别进行观察,而数据库级别变体发布来自数据库内所有集合的更改。订阅数据库更改流时,请确保为事件类型使用合适的类型,因为转换可能无法正确应用于不同的实体类型。有疑问,请使用Document.
使用同步驱动程序侦听更改流会创建一个长时间运行的阻塞任务,该任务需要委派给单独的组件。在这种情况下,我们需要首先创建一个MessageListenerContainer,它将是运行特定SubscriptionRequest任务的主要入口点。春季数据的MongoDB已经附带了操作上的默认实现MongoTemplate,并能创建和运行的Task一个实例ChangeStreamRequest。
以下示例显示了如何将 Change Streams 与MessageListener实例一起使用:
示例 119. 使用MessageListener实例更改流
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)以提供附加功能。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有