原标题:Spring认证中国教育管理中心-Spring Data MongoDB教程十三(内容来源:Spring中国教育管理中心)
Spring Data 调整了 Kotlin 的细节以允许创建和更改对象。
Kotlin 类支持实例化,默认情况下所有类都是不可变的,并且需要显式属性声明来定义可变属性。考虑以下data类Person:
data class Person(val id: String, val name: String)
上面的类编译为具有显式构造函数的典型类。我们可以通过添加另一个构造函数来自定义这个类,并使用注释@PersistenceConstructor来指示构造函数首选项:
data class Person(var id: String, val name: String) {
@PersistenceConstructor
constructor(id: String) : this(id, "unknown")
}
Kotlin 通过允许在未提供参数时使用默认值来支持参数可选性。当 Spring Data 检测到具有参数默认值的构造函数时,如果数据存储不提供值(或简单地返回null),它就会使这些参数不存在,因此 Kotlin 可以应用参数默认值。考虑以下应用参数默认值的类name
data class Person(var id: String, val name: String = "unknown")
每次name参数不是结果的一部分或其值为 时null,则name默认为unknown。
在 Kotlin 中,默认情况下所有类都是不可变的,并且需要明确的属性声明来定义可变属性。考虑以下data类Person:
data class Person(val id: String, val name: String)
这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 生成copy(…)创建新对象实例的方法,该方法从现有对象复制所有属性值并将作为参数提供的属性值应用到该方法。
Kotlin 允许声明属性覆盖来改变子类中的属性。
open class SuperType(open var field: Int)
class SubType(override var field: Int = 1) :
SuperType(field) {
}
这样的安排呈现了两个名称为 的属性field。Kotlin 为每个类中的每个属性生成属性访问器(getter 和 setter)。实际上,代码如下所示:
public class SuperType {
private int field;
public SuperType(int field) {
this.field = field;
}
public int getField() {
return this.field;
}
public void setField(int field) {
this.field = field;
}
}
public final class SubType extends SuperType {
private int field;
public SubType(int field) {
super(field);
this.field = field;
}
public int getField() {
return this.field;
}
public void setField(int field) {
this.field = field;
}
}
getter 和 setterSubType只在set 上,SubType.field而不是SuperType.field. 在这种安排中,使用构造函数是设置的唯一默认方法SuperType.field。添加方法 to SubTypeset SuperType.fieldviathis.SuperType.field = …是可能的,但不属于支持的约定。属性覆盖在某种程度上会产生冲突,因为属性共享相同的名称但可能代表两个不同的值。我们通常建议使用不同的属性名称。
Spring Data 模块通常支持包含不同值的覆盖属性。从编程模型的角度来看,需要考虑以下几点:
MappingMongoConverter当没有提供额外的映射元数据时,有一些将对象映射到文档的约定。这些约定是:
MongoDB 要求您有一个_id包含所有文档的字段。如果您不提供,驱动程序将分配一个带有生成值的 ObjectId。“_id”字段可以是除数组以外的任何类型,只要它是唯一的。驱动程序自然支持所有原始类型和日期。使用 时,MappingMongoConverter有一些规则控制 Java 类中的属性如何映射到此_id字段。
以下概述了将映射到_id文档字段的字段:
下面概述了对映射到 _id 文档字段的属性进行的类型转换(如果有)。
查询和更新时MongoTemplate将使用转换器来处理与上述保存文档规则相对应的Query和Update对象的转换,因此查询中使用的字段名称和类型将能够匹配域类中的内容。
本节解释了类型如何映射到 MongoDB 表示和从 MongoDB 表示映射。Spring Data MongoDB 支持所有可以表示为 BSON(MongoDB 的内部文档格式)的类型。除了这些类型之外,Spring Data MongoDB 还提供了一组内置转换器来映射其他类型。您可以提供自己的转换器来调整类型转换。有关更多详细信息,请参阅[ mapping-explicit-converters]。
下面提供了每种可用类型转换的示例:
除非明确配置,否则MappingMongoConverter在创建MongoTemplate. 您可以创建自己的MappingMongoConverter. 这样做可以让您指定在类路径中可以找到域类的位置,以便 Spring Data MongoDB 可以提取元数据并构建索引。此外,通过创建您自己的实例,您可以注册 Spring 转换器以将特定类映射到数据库或从数据库映射。
您可以使用基于 Java 或基于 XML 的元数据来配置MappingMongoConverter以及 com.mongodb.client.MongoClientMongoTemplate。以下示例使用 Spring 的基于 Java 的配置:
Example 180.@Configuration 类来配置 MongoDB 映射支持
@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
@Override
public String getDatabaseName() {
return "database";
}
// the following are optional
@Override
public String getMappingBasePackage() {
return "com.bigbank.domain";
}
@Override
void configureConverters(MongoConverterConfigurationAdapter adapter) {
adapter.registerConverter(new org.springframework.data.mongodb.test.PersonReadConverter());
adapter.registerConverter(new org.springframework.data.mongodb.test.PersonWriteConverter());
}
@Bean
public LoggingEventListener<MongoMappingEvent> mappingEventsListener() {
return new LoggingEventListener<MongoMappingEvent>();
}
}
映射基础包定义了用于扫描用于预初始化MappingContext. 默认情况下使用配置类包。
为特定域类型配置额外的自定义转换器,用您的自定义实现替换这些类型的默认映射过程。
AbstractMongoClientConfiguration要求您实现定义 acom.mongodb.client.MongoClient以及提供数据库名称的方法。AbstractMongoClientConfiguration还有一个名为的方法getMappingBasePackage(…),您可以重写该方法以告诉转换器在哪里扫描使用@Document注释注释的类。
您可以通过覆盖该 customConversionsConfiguration方法向转换器添加其他转换器。MongoDB 的原生 JSR-310 支持可以通过MongoConverterConfigurationAdapter.useNativeDriverJavaTimeCodecs(). 前面的示例中还显示了一个LoggingEventListener,它记录MongoMappingEvent了发布到 SpringApplicationContextEvent基础设施上的实例。
AbstractMongoClientConfiguration创建一个MongoTemplate实例并将其注册到名称为 的容器中mongoTemplate。
Spring 的 MongoDB 命名空间允许您在 XML 中启用映射功能,如以下示例所示:
示例 181.配置 MongoDB 映射支持的 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-3.0.xsd">
<!-- Default bean name is 'mongo' -->
<mongo:mongo-client host="localhost" port="27017"/>
<mongo:db-factory dbname="database" mongo-ref="mongoClient"/>
<!-- by default look for a Mongo object named 'mongo' - default name used for the converter is 'mappingConverter' -->
<mongo:mapping-converter base-package="com.bigbank.domain">
<mongo:custom-converters>
<mongo:converter ref="readConverter"/>
<mongo:converter>
<bean class="org.springframework.data.mongodb.test.PersonWriteConverter"/>
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
<bean id="readConverter" class="org.springframework.data.mongodb.test.PersonReadConverter"/>
<!-- set the mapping converter to be used by the MongoTemplate -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>
<bean class="org.springframework.data.mongodb.core.mapping.event.LoggingEventListener"/>
</beans>
该base-package属性告诉它在哪里扫描用@ org.springframework.data.mongodb.core.mapping.Document注释注释的类。
要充分利用 Spring Data MongoDB 支持中的对象映射功能,您应该使用注释对映射对象进行@Document注释。尽管映射框架没有必要具有此注释(您的 POJO 已正确映射,即使没有任何注释),但它允许类路径扫描器查找和预处理您的域对象以提取必要的元数据。如果你不使用这个注解,你的应用程序在你第一次存储域对象时会受到轻微的性能影响,因为映射框架需要建立它的内部元数据模型,以便它知道你的域对象的属性以及如何坚持他们。以下示例显示了一个域对象:
示例 182. 示例域对象
package com.mycompany.domain;
@Document
public class Person {
@Id
private ObjectId id;
@Indexed
private Integer ssn;
private String firstName;
@Indexed
private String lastName;
}
该@Id注解告诉你要使用MongoDB的哪个属性映射器_id属性和@Indexed注解告诉映射框架调用createIndex(…)你的文档的那个属性,使得搜索速度更快。自动索引创建仅适用于用@Document.
默认情况下禁用 自动索引创建,需要通过配置启用(请参阅索引创建)。
Spring Data MongoDB 可以自动为使用@Document. 自 3.0 版起,必须显式启用索引创建,以防止对集合生命周期和性能影响产生不良影响。在应用程序启动时以及在应用程序运行时第一次访问实体类型时,会为初始实体集自动创建索引。
我们通常建议为基于应用程序的索引控制显式创建索引,因为 Spring Data 无法为在应用程序运行时重新创建的集合自动创建索引。
IndexResolver如果您想使用@Indexed诸如@GeoSpatialIndexed, @TextIndexed,之类的注释,则为程序化索引定义创建提供抽象@CompoundIndex。您可以使用索引定义IndexOperations来创建索引。创建索引的一个好时机是在应用程序启动时,特别是在应用程序上下文刷新之后,由观察触发ContextRefreshedEvent。此事件保证上下文已完全初始化。请注意,此时其他组件,尤其是 bean 工厂可能可以访问 MongoDB 数据库。
示例 183. 单一域类型的程序化索引创建
class MyListener {
@EventListener(ContextRefreshedEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
.getConverter().getMappingContext();
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);
IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
}
}
示例 184. 为所有初始实体创建程序索引
class MyListener{
@EventListener(ContextRefreshedEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
.getConverter().getMappingContext();
// consider only entities that are annotated with @Document
mappingContext.getPersistentEntities()
.stream()
.filter(it -> it.isAnnotationPresent(Document.class))
.forEach(it -> {
IndexOperations indexOps = mongoTemplate.indexOps(it.getType());
resolver.resolveIndexFor(it.getType()).forEach(indexOps::ensureIndex);
});
}
}
或者,如果您想在任何组件能够从您的应用程序访问您的数据库之前确保索引和集合存在,请在返回对象之前声明一个@Bean方法MongoTemplate并包含上面的代码MongoTemplate。
要关闭自动创建索引ON请覆盖autoIndexCreation()在你的配置。
@Configuration public class Config extends AbstractMongoClientConfiguration { @Override public boolean autoIndexCreation() { return true; } // ... }
自3.0 版起,自动索引创建默认关闭。
MappingMongoConverter 可以使用元数据来驱动对象到文档的映射。以下注释可用:
映射元数据基础设施在一个独立的 spring-data-commons 项目中定义,该项目与技术无关。MongoDB 支持中使用特定子类来支持基于注释的元数据。如果有需求,也可以采取其他策略。
这是一个更复杂的映射示例。
@Document
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
public class Person<T extends Address> {
@Id
private String id;
@Indexed(unique = true)
private Integer ssn;
@Field("fName")
private String firstName;
@Indexed
private String lastName;
private Integer age;
@Transient
private Integer accountTotal;
@DBRef
private List<Account> accounts;
private T address;
public Person(Integer ssn) {
this.ssn = ssn;
}
@PersistenceConstructor
public Person(Integer ssn, String firstName, String lastName, Integer age, T address) {
this.ssn = ssn;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
}
public String getId() {
return id;
}
// no setter for Id. (getter is only exposed for some unit testing)
public Integer getSsn() {
return ssn;
}
// other getters/setters omitted
}
@Field(targetType=…)当映射基础设施推断的本机 MongoDB 类型与预期的类型不匹配时,可以派上用场。就像 for BigDecimal,它被表示为String而不是Decimal128,只是因为早期版本的 MongoDB Server 不支持它。
public class Balance {
@Field(targetType = DECIMAL128)
private BigDecimal value;
// ...
}
您甚至可以考虑自己的自定义注释。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Field(targetType = FieldType.DECIMAL128)
public @interface Decimal128 { }
// ...
public class Balance {
@Decimal128
private BigDecimal value;
// ...
}
映射子系统允许通过使用注释对构造函数进行注释来定制对象构造@PersistenceConstructor。用于构造函数参数的值按以下方式解析:
class OrderItem {
private @Id String id;
private int quantity;
private double unitPrice;
OrderItem(String id, @Value("#root.qty ?: 0") int quantity, double unitPrice) {
this.id = id;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
// getters/setters ommitted
}
Document input = new Document("id", "4711");
input.put("unitPrice", 2.5);
input.put("qty",5);
OrderItem item = converter.read(OrderItem.class, input);
如果无法解析给定的属性路径@Value,则quantity参数注释中的 SpEL 表达式将回退到该值0。
@PersistenceConstructor可以在 MappingMongoConverterUnitTests测试套件中找到使用注释的其他示例。
18.5.4. 复合索引
还支持复合索引。它们是在类级别而不是在单个属性上定义的。
复合索引对于提高涉及多个字段条件的查询的性能非常重要
这是一个lastName以升序和age降序创建复合索引的示例:
示例 185. 示例复合索引用法
package com.mycompany.domain;
@Document
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
public class Person {
@Id
private ObjectId id;
private Integer age;
private String firstName;
private String lastName;
}
@CompoundIndex可重复使用@CompoundIndexes作为其容器。
@Document
@CompoundIndex(name = "cmp-idx-one", def = "{'firstname': 1, 'lastname': -1}")
@CompoundIndex(name = "cmp-idx-two", def = "{'address.city': -1, 'address.street': 1}")
public class Person {
String firstname;
String lastname;
Address address;
// ...
}
散列索引允许在分片集群中进行基于散列的分片。使用散列字段值对集合进行分片会导致更随机的分布。有关详细信息,请参阅MongoDB 文档。
下面是一个创建哈希索引的示例_id:
示例 186. 哈希索引使用示例
@Document
public class DomainType {
@HashIndexed @Id String id;
// ...
}
可以在其他索引定义旁边创建散列索引,如下所示,在这种情况下,两个索引都被创建:
示例 187. 哈希索引与简单索引一起使用的示例
@Document
public class DomainType {
@Indexed
@HashIndexed
String value;
// ...
}
如果上面的例子过于冗长,复合注解允许减少需要在属性上声明的注解数量:
示例 188. 组合哈希索引使用示例
@Document
public class DomainType {
@IndexAndHash(name = "idx...")
String value;
// ...
}
@Indexed
@HashIndexed
@Retention(RetentionPolicy.RUNTIME)
public @interface IndexAndHash {
@AliasFor(annotation = Indexed.class, attribute = "name")
String name() default "";
}
可能为元注释的某些属性注册别名。
尽管通过注释创建索引在许多场景中派上用场,但考虑到通过手动设置索引来接管更多控制权IndexOperations。
mongoOperations.indexOpsFor(Jedi.class) .ensureIndex(HashedIndex.hashed("useTheForce"));
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。