原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程十五(内容来源:Spring中国教育管理中心)
18.7.1.使用注册的 Spring 转换器进行保存
以下示例显示了Converter从Person对象转换为 a 的实现org.bson.Document:
代码语言:javascript复制import org.springframework.core.convert.converter.Converter;
import org.bson.Document;
public class PersonWriteConverter implements Converter<Person, Document> {
public Document convert(Person source) {
Document document = new Document();
document.put("_id", source.getId());
document.put("name", source.getFirstName());
document.put("age", source.getAge());
return document;
}
}
18.7.2.使用 Spring 转换器读取
以下示例显示了Converter从 aDocument转换为Person对象的a实现:
代码语言:javascript复制public class PersonReadConverter implements Converter<Document, Person> {
public Person convert(Document source) {
Person p = new Person((ObjectId) source.get("_id"), (String) source.get("name"));
p.setAge((Integer) source.get("age"));
return p;
}
}
18.7.3.注册 Spring 转换器MongoConverter
代码语言:javascript复制class MyMongoConfiguration extends AbstractMongoClientConfiguration {
@Override
public String getDatabaseName() {
return "database";
}
@Override
protected void configureConverters(MongoConverterConfigurationAdapter adapter) {
adapter.registerConverter(new com.example.PersonReadConverter());
adapter.registerConverter(new com.example.PersonWriteConverter());
}
}
以下 SpringConverter实现示例从 aString转换为自定义Email值对象:
代码语言:javascript复制@ReadingConverter
public class EmailReadConverter implements Converter<String, Email> {
public Email convert(String source) {
return Email.valueOf(source);
}
}
如果您编写Converter的源类型和目标类型均为本机类型,我们无法确定是否应将其视为读取转换器或写入转换器。将转换器实例注册为两者可能会导致不需要的结果。例如, aConverter<String, Long>是不明确的,尽管在编写时尝试将所有String实例转换为Long实例可能没有意义。为了让你强制基础设施注册一个转换器,只有一个办法,我们提供@ReadingConverter并@WritingConverter在转换器实现使用注解。
转换器需要进行显式注册,因为不会从类路径或容器扫描中提取实例,以避免不必要的转换服务注册以及此类注册产生的副作用。转换器注册CustomConversions为中央工具,允许根据源和目标类型注册和查询已注册的转换器。
CustomConversions 附带一组预定义的转换器注册:
- JSR-310 转换器,用于在java.time,java.util.Date和String类型之间进行转换。
- 不推荐使用:Joda 时间转换器,用于在org.joda.time、JSR-310 和java.util.Date.
- 已弃用:ThreeTenBackport 转换器,用于在org.joda.time、JSR-310 和java.util.Date.
本地时间类型(例如LocalDateTimeto java.util.Date)的默认转换器依赖于系统默认时区设置在这些类型之间进行转换。您可以通过注册您自己的转换器来覆盖默认转换器。
转换器消歧
通常,我们会检查Converter它们相互转换的源和目标类型的实现。根据其中一个是否是底层数据访问 API 可以本地处理的类型,我们将转换器实例注册为读取或写入转换器。以下示例显示了一个写入和读取转换器(注意区别在于 上的限定符的顺序Converter):
代码语言:javascript复制// Write converter as only the target type is one that can be handled natively
class MyConverter implements Converter<Person, String> { … }
// Read converter as only the source type is one that can be handled natively
class MyConverter implements Converter<String, Person> { … }
19. 分片
MongoDB 通过分片支持大型数据集,分片是一种跨多个数据库服务器分发数据的方法。请参阅MongoDB 文档以了解如何设置分片集群及其要求和限制。
Spring Data MongoDB 使用@Sharded注释来标识存储在分片集合中的实体,如下所示。
代码语言:javascript复制@Document("users")
@Sharded(shardKey = { "country", "userId" })
public class User {
@Id
Long id;
@Field("userid")
String userId;
String country;
}
分片键的属性被映射到实际的字段名称。
19.1.分片集合
Spring Data MongoDB 不会为其所需的集合或索引自动设置分片。下面的代码片段展示了如何使用 MongoDB 客户端 API 执行此操作。
代码语言:javascript复制MongoDatabase adminDB = template.getMongoDbFactory()
.getMongoDatabase("admin");
adminDB.runCommand(new Document("enableSharding", "db"));
Document shardCmd = new Document("shardCollection", "db.users")
.append("key", new Document("country", 1).append("userid", 1));
adminDB.runCommand(shardCmd);
需要针对admin数据库运行分片命令。
如有必要,为特定数据库启用分片。
在启用了分片的数据库中分片集合。
指定分片键。此示例使用基于范围的分片。
19.2.分片键处理
分片键由一个或多个属性组成,这些属性必须存在于目标集合的每个文档中。它用于跨分片分发文档。
将@Sharded注释添加到实体使 Spring Data MongoDB 能够应用分片场景所需的最大努力优化。这意味着本质上添加所需的分片键信息(如果尚不存在)以replaceOne在更新实体时过滤查询。这可能需要额外的服务器往返来确定当前分片键的实际值。
通过设置@Sharded(immutableKey = true)Spring Data 不会尝试检查实体分片键是否已更改。
有关更多详细信息,请参阅MongoDB 文档。以下列表包含哪些操作符合分片键自动包含的条件:
- (Reactive)CrudRepository.save(…)
- (Reactive)CrudRepository.saveAll(…)
- (Reactive)MongoTemplate.save(…)
20. Kotlin 支持
Kotlin是一种面向 JVM(和其他平台)的静态类型语言,它允许编写简洁优雅的代码,同时提供与用 Java 编写的现有库的出色互操作性。
Spring Data 为 Kotlin 提供一流的支持,让开发人员几乎可以像编写 Kotlin 原生框架一样编写 Kotlin 应用程序。
使用 Kotlin 构建 Spring 应用程序的最简单方法是利用 Spring Boot 及其专用的 Kotlin 支持。本综合教程将教您如何使用start.spring.io使用 Kotlin 构建 Spring Boot 应用程序。
20.1.要求
Spring Data 支持 Kotlin 1.3 并要求kotlin-stdlib(或其变体之一,例如kotlin-stdlib-jdk8)和kotlin-reflect存在于类路径中。如果您通过start.spring.io引导 Kotlin 项目,则默认提供这些。
20.2.零安全
Kotlin 的关键特性之一是空安全,它null在编译时干净地处理值。这通过可空性声明和“值或无值”语义的表达使应用程序更安全,而无需支付包装器的成本,例如Optional. (Kotlin 允许使用具有可为空值的函数式构造。请参阅Kotlin 空值安全性综合指南。)
尽管 Java 不允许您在其类型系统中表达空安全性,但 Spring Data API 使用包中声明的JSR-305工具友好注释进行了注释org.springframework.lang。默认情况下,来自 Kotlin 中使用的 Java API 的类型被识别为平台类型,对其进行空检查。 Kotlin 对 JSR-305 注释和 Spring 可空性注释的支持为Kotlin 开发人员提供了整个 Spring Data API 的空安全,具有null在编译时处理相关问题的优势。
请参阅存储库方法的空处理如何将空安全应用于 Spring 数据存储库。
您可以通过添加-Xjsr305带有以下选项的编译器标志来配置 JSR-305 检查:-Xjsr305={strict|warn|ignore}.
对于 Kotlin 1.1 版本,默认行为与-Xjsr305=warn. strict考虑到 Spring Data API 空安全,该值是必需的。Kotlin 类型是从 Spring API 推断出来的,但在使用时应该知道 Spring API 可空性声明可以演变,即使在次要版本之间也是如此,并且将来可能会添加更多检查。
尚不支持通用类型参数、可变参数和数组元素可空性,但应在即将发布的版本中提供。
20.3.对象映射
有关Kotlin对象如何具体化的详细信息,请参阅Kotlin 支持。
20.4.扩展
Kotlin扩展提供了使用附加功能扩展现有类的能力。Spring Data Kotlin API 使用这些扩展为现有的 Spring API 添加新的 Kotlin 特定的便利。
请记住,需要导入 Kotlin 扩展才能使用。与静态导入类似,IDE 应该在大多数情况下自动建议导入。 例如,Kotlin reified 类型参数为 JVM泛型类型擦除提供了一种解决方法,Spring Data 提供了一些扩展来利用此功能。这允许更好的 Kotlin API。
要SWCharacter在 Java 中检索对象列表,您通常会编写以下内容:
代码语言:javascript复制Flux<SWCharacter> characters = template.find(SWCharacter.class).inCollection("star-wars").all()
使用 Kotlin 和 Spring Data 扩展,您可以改为编写以下内容:
代码语言:javascript复制val characters = template.find<SWCharacter>().inCollection("star-wars").all()
// or (both are equivalent)
val characters : Flux<SWCharacter> = template.find().inCollection("star-wars").all()
在 Java 中,charactersKotlin 是强类型的,但 Kotlin 巧妙的类型推断允许使用更短的语法。
Spring Data MongoDB 提供以下扩展:
- 具体化的泛型支持MongoOperations,ReactiveMongoOperations,FluentMongoOperations,ReactiveFluentMongoOperations,和Criteria。
- Kotlin 的类型安全查询
- 的协程扩展ReactiveFluentMongoOperations。
20.5.协程
Kotlin协程是轻量级线程,允许强制编写非阻塞代码。在语言方面,suspend函数为异步操作提供了抽象,而在库方面kotlinx.coroutines提供了async { }像Flow.
Spring Data 模块在以下范围内提供对协程的支持:
- Kotlin 扩展中的延迟和流返回值支持
20.5.1.依赖关系
协同程序支持时启用kotlinx-coroutines-core, kotlinx-coroutines-reactive而且kotlinx-coroutines-reactor依赖在类路径中:
示例 211. 在 Maven pom.xml 中添加的依赖项
代码语言:javascript复制<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
</dependency>
支持的版本1.3.0及以上。
20.5.2.反应如何转化为协程?
对于返回值,从 Reactive 到 Coroutines API 的转换如下:
- fun handler(): Mono<Void> 变成 suspend fun handler()
- fun handler(): Mono<T>成为suspend fun handler(): T或suspend fun handler(): T?取决于是否Mono可以为空(具有更静态类型的优点)
- fun handler(): Flux<T> 变成 fun handler(): Flow<T>
FlowFlux在 Coroutines 世界中是等价的,适用于热流或冷流,有限流或无限流,主要区别如下:
- Flow是基于推的,Flux而是推拉混合的
- 背压是通过挂起函数实现的
- Flow只有一个挂起collect方法,操作符作为扩展实现
- 由于协程,运算符易于实现
- 扩展允许添加自定义运算符 Flow
- 收集操作正在暂停功能
- map运算符支持异步操作(不需要flatMap),因为它需要一个挂起函数参数
阅读这篇关于Going Reactive with Spring、Coroutines 和 Kotlin Flow 的博客文章,了解更多详细信息,包括如何与 Coroutines 并发运行代码。
20.5.3.存储库
这是一个 Coroutines 存储库的示例:
代码语言:javascript复制interface CoroutineRepository : CoroutineCrudRepository<User, String> {
suspend fun findOne(id: String): User
fun findByFirstname(firstname: String): Flow<User>
suspend fun findAllByFirstname(id: String): List<User>
}
协程存储库建立在反应式存储库上,以通过 Kotlin 的协程公开数据访问的非阻塞特性。协程存储库上的方法可以由查询方法或自定义实现支持。如果自定义方法是可调用的,则调用自定义实现方法会将 Coroutines 调用传播到实际实现方法,suspend而无需实现方法返回反应类型,例如Mono或Flux。
协程存储库仅在存储库扩展CoroutineCrudRepository接口时才被发现。
21. JMX 支持
MongoDB 的 JMX 支持公开了在单个 MongoDB 服务器实例的管理数据库上运行“serverStatus”命令的结果。它还公开了一个管理 MBean,MongoAdmin允许您执行管理操作,例如删除或创建数据库。JMX 功能建立在 Spring Framework 中可用的 JMX 功能集之上。请参阅此处了解更多详情。
21.1.MongoDB JMX 配置
Spring 的 Mongo 命名空间允许您启用 JMX 功能,如以下示例所示:
示例 212.配置 MongoDB 的 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:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
https://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Default bean name is 'mongo' -->
<mongo:mongo-client host="localhost" port="27017"/>
<!-- by default look for a Mongo object named 'mongo' -->
<mongo:jmx/>
<context:mbean-export/>
<!-- To translate any MongoExceptions thrown in @Repository annotated classes -->
<context:annotation-config/>
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean" p:port="1099" />
<!-- Expose JMX over RMI -->
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"
depends-on="registry"
p:objectName="connector:name=rmi"
p:serviceUrl="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector" />
</beans>
前面的代码公开了几个 MBean:
- AssertMetrics
- BackgroundFlushingMetrics
- BtreeIndexCounters
- ConnectionMetrics
- GlobalLockMetrics
- MemoryMetrics
- OperationCounters
- ServerInfo
- MongoAdmin
JConsole 的以下屏幕截图显示了生成的配置: