zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Spring认证中国教育管理中心-Spring Data MongoDB教程八

2023-03-20 14:48:26 时间

原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程八(内容来源:Spring中国教育管理中心)

11.13.索引和馆藏管理

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();
}

11.13.1.创建索引的方法

您可以使用 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支持排序规则的配置。

11.13.2.访问索引信息

该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]]

11.13.3.处理集合的方法

以下示例显示了如何创建集合:

示例 106. 使用集合处理 MongoTemplate

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方法的子类。当事件被调度时,你的监听器被调用并在它进入转换器之前传递域对象。以下示例显示了如何执行此操作:

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:

  • 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

@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;                                                                  
	}
}

根据您的要求实现回调。

如果存在多个相同域类型的实体回调,则可能对实体回调进行排序。排序遵循最低优先级。

11.16.2.注册实体回调

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 启用类型过滤。

在单个实现类中组合多个实体回调接口。

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返回有关索引的信息:

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 设置

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 配置文件。

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实例的可尾游标

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 的无限流查询

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();

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实例更改流

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)以提供附加功能。