竹笋

首页 » 问答 » 灌水 » Spring认证中国教育管理中心Spr
TUhjnbcbe - 2023/1/24 23:14:00
自媒体求职招聘QQ群 https://www.sohu.com/a/406850306_120376760

原标题:Spring认证中国教育管理中心-SpringDataMongoDB教程十四(内容来源:Spring中国教育管理中心)

18.5.6.通配符索引

AWildcardIndex是一个索引,可用于包含所有字段或基于给定(通配符)模式的特定字段。有关详细信息,请参阅MongoDB文档。

可以使用WildcardIndexvia以编程方式设置索引IndexOperations。

示例.编程通配符索引设置

mongoOperations.indexOps(User.class).ensureIndex(newWildcardIndex("userMetadata"));

db.user.createIndex({"userMetadata.**":1},{})

WildcardIndex注释允许可与文档类型或属性或者是用声明性指数设置。

如果放置在根级域实体类型(用注释的类型

Document)上,索引解析器将为它创建一个通配符索引。

示例.域类型的通配符索引

Document

WildcardIndexedpublicclassProduct{  //…}

db.product.createIndex({"**":1},{})

该wildcardProjection可被用来指定键输入/排除在索引中。

示例.通配符索引wildcardProjection

Document

WildcardIndexed(wildcardProjection="{userMetadata.age:0}")publicclassUser{private

IdStringid;privateUserMetadatauserMetadata;}

db.user.createIndex({"**":1},{"wildcardProjection":{"userMetadata.age":0}})

通配符索引也可以通过直接在字段中添加注释来表示。请注意,wildcardProjection不允许在嵌套路径(例如属性)上使用。

WildcardIndexed在索引创建期间省略对带有注释的类型的投影。

示例.属性上的通配符索引

DocumentpublicclassUser{private

IdStringid;

WildcardIndexedprivateUserMetadatauserMetadata;}

db.user.createIndex({"userMetadata.**":1},{})

18.5.7.文本索引

MongoDBv.2.4默认禁用文本索引功能。

创建文本索引允许将多个字段累积到可搜索的全文索引中。每个集合只能有一个文本索引,因此所有标记

TextIndexed为的字段都合并到此索引中。可以对属性进行加权以影响排名结果的文档分数。文本索引的默认语言是英语。要更改默认语言,请将language属性设置为您想要的任何语言(例如,

Document(language="spanish"))。使用名为languageor的属性

Language,您可以在每个文档的基础上定义语言覆盖。以下示例显示了如何创建文本索引并将语言设置为西班牙语:

示例.示例文本索引用法

Document(language="spanish")classSomeEntity{

TextIndexedStringfoo;

LanguageStringlang;Nestednested;}classNested{

TextIndexed(weight=5)Stringbar;Stringroo;}

18.5.8.使用DBRefs

映射框架不必存储嵌入在文档中的子对象。您也可以单独存储它们并使用aDBRef来引用该文档。当对象从MongoDB加载时,这些引用会被急切地解析,以便您返回一个映射对象,该对象看起来与嵌入在顶级文档中的存储相同。

以下示例使用DBRef来引用独立于引用它的对象存在的特定文档(为简洁起见,两个类都显示为内嵌):

DocumentpublicclassAccount{

IdprivateObjectIdid;privateFloattotal;}

DocumentpublicclassPerson{

IdprivateObjectIdid;

IndexedprivateIntegerssn;

DBRefprivateListAccountaccounts;}

您不需要使用

OneToMany或类似的机制,因为对象列表告诉映射框架您想要一对多关系。当对象存储在MongoDB中时,有一个DBRef列表而不是Account对象本身。在加载DBRefs的集合时,建议将集合类型中保存的引用限制为特定的MongoDB集合。这允许批量加载所有引用,而指向不同MongoDB集合的引用需要一一解析。

映射框架不处理级联保存。如果更改Account对象引用的Person对象,则必须Account单独保存该对象。调用save上的Person对象不会自动保存Account在对象accounts属性。

DBRefs也可以懒惰地解决。在这种情况下,在第一次访问属性时解析引用的实际Object或Collection引用。使用的lazy属性

DBRef来指定这一点。也定义为延迟加载DBRef并用作构造函数参数的必需属性也使用延迟加载代理进行修饰,以确保尽可能减少对数据库和网络的压力。

延迟加载的DBRefs可能很难调试。确保工具不会通过例如调用toString()或某些内联调试渲染调用属性getter意外触发代理解析。请考虑启用跟踪日志记录org.springframework.data.mongodb.core.convert.DefaultDbRefResolver以深入了解DBRef解决方案。

延迟加载可能需要类代理,反过来,由于JEP:StronglyEncapsulateJDKInternalsbyDefault,从Java16+开始,可能需要访问未打开的jdk内部。对于这些情况,请考虑回退到接口类型(例如,从ArrayListto切换List)或提供所需的--add-opens参数。

18.5.9.使用文档参考

Using

DocumentReference提供了一种灵活的方式来引用MongoDB中的实体。虽然目标与使用DBRefs时相同,但存储表示不同。DBRef解析为具有固定结构的文档,如MongoDB参考文档中所述。文档引用,不遵循特定格式。它们实际上可以是任何东西,单个值,整个文档,基本上可以存储在MongoDB中的所有内容。默认情况下,映射层将使用引用的实体id值进行存储和检索,如下面的示例所示。

DocumentclassAccount{

IdStringid;Floattotal;}

DocumentclassPerson{

IdStringid;

DocumentReferenceListAccountaccounts;}

Accountaccount=…tempate.insert(account);template.update(Person.class).matching(where("id").is(…)).apply(newUpdate().push("accounts").value(account)).first();

{"_id":…,"accounts":["b9e"…]}

标记Account要引用的值的集合。

映射框架不处理级联保存,因此请确保单独保留引用的实体。

添加对现有实体的引用。

引用的Account实体表示为其_id值的数组。

上面的示例使用_id基于fetch查询({_id:?#{#target}})进行数据检索并急切地解析链接的实体。可以使用以下属性更改分辨率默认值(如下所列)

DocumentReference

延迟加载可能需要类代理,反过来,由于JEP:StronglyEncapsulateJDKInternalsbyDefault,从Java16+开始,可能需要访问未打开的jdk内部。对于这些情况,请考虑回退到接口类型(例如,从ArrayListto切换List)或提供所需的--add-opens参数。

DocumentReference(lookup)允许定义可能与_id字段不同的过滤器查询,因此提供了一种灵活的方式来定义实体之间的引用,如下面的示例所示,其中Publisher书籍的由其首字母缩略词而不是内部id.

DocumentclassBook{

IdObjectIdid;Stringtitle;ListStringauthor;

Field("publisher_ac")

DocumentReference(lookup="{acronym:?#{#target}}")Publisherpublisher;}

DocumentclassPublisher{

IdObjectIdid;Stringacronym;Stringname;

DocumentReference(lazy=true)ListBookbooks;}

Book文档

{"_id":9a48e32,"title":"TheWardedMan","author":["PeterV.Brett"],"publisher_ac":"DR"}

Publisher文档

{"_id":1a23e45,"acronym":"DR","name":"DelRey",…}

使用该acronym字段查询Publisher集合中的实体。

延迟加载对Book集合的引用。

上面的代码片段显示了使用自定义引用对象时的阅读方面。写作需要一些额外的设置,因为映射信息没有表达出从何#target而来。映射层需要Converter在目标文档和之间注册aDocumentPointer,如下所示:

WritingConverterclassPublisherReferenceConverterimplementsConverterPublisher,DocumentPointerString{  

Override  publicDocumentPointerStringconvert(Publishersource){    return()-source.getAcronym();  }}

如果没有DocumentPointer提供转换器,则可以根据给定的查找查询计算目标参考文档。在这种情况下,关联目标属性的评估如下面的示例所示。

DocumentclassBook{

IdObjectIdid;Stringtitle;ListStringauthor;

DocumentReference(lookup="{acronym:?#{acc}}")Publisherpublisher;}

DocumentclassPublisher{

IdObjectIdid;Stringacronym;Stringname;//...}

{"_id":9a48e32,"title":"TheWardedMan","author":["PeterV.Brett"],"publisher":{"acc":"DOC"}}

使用该acronym字段查询Publisher集合中的实体。

查找查询的字段值占位符(如acc)用于形成参考文档。

它也可以对模型关系式的一对许多使用的组合引用

ReadonlyProperty和

DocumentReference。这种方法允许链接类型不将链接值存储在拥有文档中,而是存储在引用文档中,如下例所示。

DocumentclassBook{

IdObjectIdid;Stringtitle;ListStringauthor;ObjectIdpublisherId;}

DocumentclassPublisher{

IdObjectIdid;Stringacronym;Stringname;

ReadOnlyProperty

DocumentReference(lookup="{publisherId:?#{#self._id}}")ListBookbooks;}

Book文档

{"_id":9a48e32,"title":"TheWardedMan","author":["PeterV.Brett"],"publisherId":8cfb}

Publisher文档

{"_id":8cfb,"acronym":"DR","name":"DelRey"}

通过将存储在文档中Book来设置从(引用)到Publisher(所有者)的链接。Publisher.idBook

将持有引用的属性标记为只读。这可以防止Book在Publisher文档中存储对个人的引用。

使用该#self变量访问Publisher文档中的值,并在此检索中Books使用匹配的publisherId.

有了上述所有内容,就可以对实体之间的所有类型的关联进行建模。查看下面的非详尽示例列表,以了解可能的情况。

示例.使用id字段的简单文档引用

classEntity{

DocumentReferenceReferencedObjectref;}

//entity{"_id":"8cfb","ref":"9a48e32"}//referencedobject{"_id":"9a48e32"}

MongoDB简单类型可以直接使用,无需进一步配置。

示例.使用带有显式查找查询的id字段的简单文档引用

classEntity{

DocumentReference(lookup="{_id:?#{#target}}")ReferencedObjectref;}

//entity{"_id":"8cfb","ref":"9a48e32"}//referencedobject{"_id":"9a48e32"}

target定义了参考值本身。

示例.文档参考提取refKey查找查询的字段

classEntity{

DocumentReference(lookup="{_id:?#{refKey}}")privateReferencedObjectref;}

WritingConverterclassToDocumentPointerConverterimplementsConverterReferencedObject,DocumentPointerDocument{  publicDocumentPointerDocumentconvert(ReferencedObjectsource){    return()-newDocument("refKey",source.id);  }}

//entity{"_id":"8cfb","ref":{"refKey":"9a48e32"}}//referencedobject{"_id":"9a48e32"}

用于获取参考值的密钥必须是写入时使用的密钥。

refKey是的缩写target.refKey。

示例.具有多个值的文档引用形成查找查询

classEntity{

DocumentReference(lookup="{firstname:?#{fn},lastname:?#{ln}}")ReferencedObjectref;}

//entity{"_id":"8cfb","ref":{"fn":"Josh","ln":"Long"}}//referencedobject{"_id":"9a48e32","firsntame":"Josh","lastname":"Long",}

读/WIRTE键fn和ln自/至基于查找查询的链接文件。

使用非id字段来查找目标文档。

示例.从目标集合中读取文档引用

classEntity{

DocumentReference(lookup="{_id:?#{id}}",collection="?#{collection}")privateReferencedObjectref;}

WritingConverterclassToDocumentPointerConverterimplementsConverterReferencedObject,DocumentPointerDocument{  publicDocumentPointerDocumentconvert(ReferencedObjectsource){    return()-newDocument("id",source.id).append("collection",…);  }}

//entity{"_id":"8cfb","ref":{"id":"9a48e32","collection":"…"}}

_id从/向参考文档读取/写入密钥以在查找查询中使用它们。

可以使用其键从参考文档中读取集合名称。

我们知道在查找查询中使用各种MongoDB查询运算符很诱人,这很好。但是有几个方面需要考虑:

确保有支持您查找的索引。

请注意,解析需要服务器往返导致延迟,请考虑使用惰性策略。

使用or运算符批量加载文档引用集合。

尽最大努力在内存中恢复原始元素顺序。仅在使用等式表达式时才可以恢复顺序,而在使用MongoDB查询运算符时则无法恢复。在这种情况下,结果将在从商店或通过提供的

DocumentReference(sort)属性收到时进行排序。

一些更一般的评论:

你使用循环引用吗?问问你自己是否需要它们。

懒惰的文档引用很难调试。确保工具不会意外触发代理解析,例如调用toString().

不支持使用反应式基础架构阅读文档引用。

18.5.10.映射框架事件

在映射过程的整个生命周期中都会触发事件。这在生命周期事件部分进行了描述。

在SpringApplicationContext中声明这些bean会导致在调度事件时调用它们。

18.6.展开类型

解包实体用于在Java域模型中设计值对象,其属性被展平到父级的MongoDB文档中。

18.6.1.展开类型映射

考虑以下User.name用

Unwrapped.该

Unwrapped注释信号是所有属性UserName应该被平整出到user拥有该文档name属性。

示例.解包对象的示例代码

classUser{

IdStringuserId;

Unwrapped(onEmpty=USE_NULL)UserNamename;}classUserName{Stringfirstname;Stringlastname;}

{"_id":"1da2ba06-3ba7","firstname":"Emma","lastname":"Frost"}

当装载name属性其值被设置为null如果两个firstname和lastname要么null或不存在。通过使用onEmpty=USE_EMPTY一个空的UserName,null其属性的潜在价值,将被创建。

对于不太冗长的可嵌入类型声明,请使用

Unwrapped.Nullableand

Unwrapped.Empty代替

Unwrapped(onEmpty=USE_NULL)and

Unwrapped(onEmpty=USE_EMPTY)。这两个注释都使用JSR-

javax.annotation.Nonnull进行元注释,以帮助进行可空性检查。

可以在展开的对象中使用复杂类型。但是,那些不能是,也不能包含未包装的字段本身。

18.6.2.解包类型字段名称

通过使用注解的可选prefix属性,一个值对象可以被多次解包

Unwrapped。通过添加,所选的前缀被添加到

Field("…")解包对象中的每个属性或名称之前。请注意,如果多个属性呈现为相同的字段名称,则值将相互覆盖。

示例.带有名称前缀的解包对象的示例代码

classUser{

IdStringuserId;

Unwrapped.Nullable(prefix="u_")UserNamename;

Unwrapped.Nullable(prefix="a_")UserNamename;}classUserName{Stringfirstname;Stringlastname;}

{"_id":"a6abd-f95f","u_firstname":"Jean","u_lastname":"Grey","a_firstname":"Something","a_lastname":"Else"}

的所有属性UserName都以为前缀u_。

的所有属性UserName都以为前缀a_。

虽然将

Field注释与

Unwrapped相同的属性组合在一起没有意义,因此会导致错误。这是用于

Field任何未包装类型属性的完全有效的方法。

示例.使用

Field注释解开对象的示例代码

publicclassUser{  

IdprivateStringuserId;

Unwrapped.Nullable(prefix="u-")UserNamename;}publicclassUserName{  

Field("first-name")privateStringfirstname;  

Field("last-name")privateStringlastname;}

{"_id":"f7b9-89da","u-first-name":"Barbara","u-last-name":"Gordon"}

的所有属性UserName都以为前缀u-。

最终字段名称是连接

Unwrapped(prefix)和的结果

Field(name)。

18.6.3.查询解包对象

可以在类型和字段级别上定义对未包装属性的查询,因为所提供的Criteria内容与域类型相匹配。呈现实际查询时将考虑前缀和潜在的自定义字段名称。使用解包对象的属性名称匹配所有包含的字段,如下面的示例所示。

示例.查询解包对象

UserNameuserName=newUserName("Carol","Danvers")QueryfindByUserName=query(where("name").is(userName));Useruser=template.findOne(findByUserName,User.class);

db.collection.find({"firstname":"Carol","lastname":"Danvers"})

也可以直接使用其属性名称来寻址解包对象的任何字段,如下面的代码片段所示。

示例.查询未包装对象的字段

QueryfindByUserFirstName=query(where("name.firstname").is("Shuri"));ListUserusers=template.findAll(findByUserFirstName,User.class);

db.collection.find({"firstname":"Shuri"})

按展开的字段排序。

展开对象的字段可用于通过其属性路径进行排序,如下面的示例所示。

示例.在展开的字段上排序

QueryfindByUserLastName=query(where("name.lastname").is("Romanoff"));ListUseruser=template.findAll(findByUserName.withSort(Sort.by("name.firstname")),User.class);

db.collection.find({"lastname":"Romanoff"}).sort({"firstname":1})

尽管可能,使用解包对象本身作为排序标准包括其所有字段的不可预测顺序,并可能导致排序不准确。

展开物体上的场投影

展开对象的场可以作为整体或通过单个场进行投影,如下面的示例所示。

示例.在展开的对象上投影。

QueryfindByUserLastName=query(where("name.firstname").is("Gamora"));findByUserLastName.fields().include("name");ListUseruser=template.findAll(findByUserName,User.class);

db.collection.find({"lastname":"Gamora"},{"firstname":1,"lastname":1})

展开对象上的场投影包括其所有属性。

示例.在展开的对象的字段上投影。

QueryfindByUserLastName=query(where("name.lastname").is("Smoak"));findByUserLastName.fields().include("name.firstname");ListUseruser=template.findAll(findByUserName,User.class);

db.collection.find({"lastname":"Smoak"},{"firstname":1})

展开对象上的场投影包括其所有属性。

在未包装的对象上按示例查询。

展开的对象可以Example像任何其他类型一样在探测器中使用。请查看按示例查询部分,以了解有关此功能的更多信息。

对解包对象的存储库查询。

该Repository抽象允许导出对未包装对象的字段以及整个对象的查询。

示例.对解包对象的存储库查询。

interfaceUserRepositoryextendsCrudRepositoryUser,String{  ListUserfindByName(UserNameusername);  ListUserfindByNameFirstname(Stringfirstname);}

匹配解包对象的所有字段。

与firstname.

即使存储库create-query-indexes命名空间属性设置为,为解包对象创建索引也会暂停true。

18.6.4.展开对象的更新

展开的对象可以作为域模型的一部分的任何其他对象进行更新。映射层负责将结构展平到其周围环境中。可以更新解包对象的单个属性以及整个值,如下面的示例所示。

示例.更新解包对象的单个字段。

Updateupdate=newUpdate().set("name.firstname","Janet");template.update(User.class).matching(where("id").is("Wasp")).apply(update).first()

db.collection.update({"_id":"Wasp"},{"set"{"firstname":"Janet"}},{...})

示例.更新一个展开的对象。

Updateupdate=newUpdate().set("name",newName("Janet","vanDyne"));template.update(User.class).matching(where("id").is("Wasp")).apply(update).first()

db.collection.update({"_id":"Wasp"},{"set"{"firstname":"Janet","lastname":"vanDyne",}},{...})

18.6.5.未包装对象上的聚合

该聚合框架会试图映射类型聚集的展开值。在引用其值之一时,请确保使用包括包装器对象的属性路径。除此之外,不需要特殊操作。

18.6.6.展开对象的索引

可以将

Indexed注释附加到解包类型的属性,就像对常规对象所做的那样。不能

Indexed与

Unwrapped拥有属性的注释一起使用。

publicclassUser{  

IdprivateStringuserId;

Unwrapped(onEmpty=USE_NULL)UserNamename;//Invalid-InvalidDataAccessApiUsageException

Indexed

Unwrapped(onEmpty=USE_Empty)Addressaddress;}publicclassUserName{privateStringfirstname;

IndexedprivateStringlastname;}

索引创建lastname的users集合。

Indexed一起使用无效

Unwrapped

18.7.自定义转换-覆盖默认映射

影响映射结果的最简单的方法是通过

Field注释指定所需的本机MongoDB目标类型。这允许BigDecimal在域模型中使用非MongoDB类型,同时以本机org.bson.types.Decimal格式持久化值。

示例.显式目标类型映射

publicclassPayment{

IdStringid;

Field(targetType=FieldType.DECIMAL)BigDecimalvalue;Datedate;}

{"_id":ObjectId("5ca4a34faab36af8"),"value":NumberDecimal(2.),"date":ISODate("9-04-03T12:11:01.Z")}

表示有效的字符串id值ObjectId会自动转换。有关

详细信息,请参阅如何_id在映射层中处理字段。

所需的目标类型明确定义为Decimal转换为NumberDecimal.否则,该

BigDecimal值将被调整为String.

Date值由MongoDB驱动程序本身处理并存储为ISODate.

上面的代码片段对于提供简单的类型提示很方便。要对映射过程进行更细粒度的控制,您可以使用MongoConverter实现注册Spring转换器,例如MappingMongoConverter.

MappingMongoConverter在尝试映射对象本身之前,检查是否有任何Spring转换器可以处理特定的类。要“劫持”的正常映射策略MappingMongoConverter,也许是为了提高性能或其它自定义映射的需求,首先需要创建春天的实现Converter接口,然后用它注册MappingConverter。

有关Spring类型转换服务的更多信息,请参阅此处的参考文档。

#开发#

1
查看完整版本: Spring认证中国教育管理中心Spr