对于最新的稳定版本,请使用 Spring Data MongoDB 4.4.0! |
使用 DBRefs
映射框架不必存储嵌入在文档中的子对象。
您还可以单独存储它们,并使用 a 来引用该文档。
从 MongoDB 加载对象时,这些引用将被预先解析,以便您返回一个映射对象,该对象看起来与嵌入在顶级文档中的存储对象相同。DBRef
以下示例使用 DBRef 来引用独立于引用它的对象而存在的特定文档(为简洁起见,这两个类都内联显示):
@Document
public class Account {
@Id
private ObjectId id;
private Float total;
}
@Document
public class Person {
@Id
private ObjectId id;
@Indexed
private Integer ssn;
@DBRef
private List<Account> accounts;
}
您不需要使用 或类似机制,因为 List of objects 会告诉映射框架您需要一对多关系。
当对象存储在 MongoDB 中时,有一个 DBRefs 列表,而不是对象本身。
在加载 s 的集合时,建议将集合类型中保存的引用限制为特定的 MongoDB 集合。
这允许批量加载所有引用,而指向不同 MongoDB 集合的引用需要逐个解析。@OneToMany
Account
DBRef
映射框架不处理级联保存。
如果更改对象引用的对象,则必须单独保存该对象。
调用对象不会自动将对象保存在属性中。Account Person Account save Person Account accounts |
DBRef
也可以延迟解析。
在这种情况下,引用的 actual 或 将在首次访问属性时解析。
使用 属性 of 指定此项。
也被定义为延迟加载并用作构造函数参数的必需属性也使用延迟加载代理进行装饰,以确保对数据库和网络施加尽可能小的压力。Object
Collection
lazy
@DBRef
DBRef
延迟加载的 s 可能很难调试。
确保工具不会意外触发代理解析,例如调用或某些内联调试渲染调用属性 getter。
请考虑启用跟踪日志记录,以便深入了解分辨率。DBRef toString() org.springframework.data.mongodb.core.convert.DefaultDbRefResolver DBRef |
延迟加载可能需要类代理,而类代理又可能需要访问 jdk 内部,由于 JEP 396:默认情况下强封装 JDK 内部,从 Java 16+ 开始,这些内部结构未打开。
对于这些情况,请考虑回退到接口类型(例如, switch from to )或提供所需的参数。ArrayList List --add-opens |
使用文档引用
使用 提供了一种在 MongoDB 中引用实体的灵活方式。
虽然目标与使用 DBRefs 时相同,但 store 表示形式不同。 解析为具有固定结构的文档,如 MongoDB 参考文档中所述。
文档引用不遵循特定格式。
它们几乎可以是任何东西,单个值,整个文档,基本上可以存储在 MongoDB 中的所有内容。
默认情况下,映射层将使用引用的实体 id 值进行存储和检索,如以下示例所示。@DocumentReference
DBRef
@Document
class Account {
@Id
String id;
Float total;
}
@Document
class Person {
@Id
String id;
@DocumentReference (1)
List<Account> accounts;
}
Account account = …
template.insert(account); (2)
template.update(Person.class)
.matching(where("id").is(…))
.apply(new Update().push("accounts").value(account)) (3)
.first();
{
"_id" : …,
"accounts" : [ "6509b9e" … ] (4)
}
1 | 标记要引用的值的集合。Account |
2 | 映射框架不处理级联保存,因此请确保单独保留引用的实体。 |
3 | 添加对现有实体的引用。 |
4 | 引用的实体表示为其值的数组。Account _id |
上面的示例使用基于 的 fetch query () 进行数据检索,并急切地解析链接实体。
可以使用_id
{ '_id' : ?#{#target} }
@DocumentReference
属性 | 描述 | 违约 |
---|---|---|
|
用于集合查找的目标数据库名称。 |
|
|
目标集合名称。 |
带注释的属性的 domain 类型,分别是 like 或 properties 时的值类型、集合名称。 |
|
单个文档查找查询通过 SpEL 表达式用作给定源值的标记来评估占位符。 like 或 properties 通过运算符组合各个查找。 |
使用加载的源值的基于字段的查询 ()。 |
|
用于在服务器端对结果文档进行排序。 |
默认情况下为 None。
将尽最大努力根据使用的查找查询恢复类似属性的结果顺序。 |
|
如果设置为 value,则在首次访问该属性时延迟 resolution。 |
默认情况下,预先解析属性。 |
延迟加载可能需要类代理,而类代理又可能需要访问 jdk 内部,由于 JEP 396:默认情况下强封装 JDK 内部,从 Java 16+ 开始,这些内部结构未打开。
对于这些情况,请考虑回退到接口类型(例如, switch from to )或提供所需的参数。ArrayList List --add-opens |
@DocumentReference(lookup)
允许定义不同于字段的过滤器查询,因此提供了一种灵活的方式来定义实体之间的引用,如以下示例所示,其中 of a book 由其首字母缩略词引用,而不是 internal 。_id
Publisher
id
@Document
class Book {
@Id
ObjectId id;
String title;
List<String> author;
@Field("publisher_ac")
@DocumentReference(lookup = "{ 'acronym' : ?#{#target} }") (1)
Publisher publisher;
}
@Document
class Publisher {
@Id
ObjectId id;
String acronym; (1)
String name;
@DocumentReference(lazy = true) (2)
List<Book> books;
}
Book
公文{
"_id" : 9a48e32,
"title" : "The Warded Man",
"author" : ["Peter V. Brett"],
"publisher_ac" : "DR"
}
Publisher
公文{
"_id" : 1a23e45,
"acronym" : "DR",
"name" : "Del Rey",
…
}
1 | 使用字段查询集合中的实体。acronym Publisher |
2 | 延迟加载对集合的引用。Book |
上面的代码片段显示了使用自定义引用对象时的读取方面。
写入需要一些额外的设置,因为 mapping 信息不表示源于何处。
映射层需要在目标文档和 之间注册 ,如下所示:#target
Converter
DocumentPointer
@WritingConverter
class PublisherReferenceConverter implements Converter<Publisher, DocumentPointer<String>> {
@Override
public DocumentPointer<String> convert(Publisher source) {
return () -> source.getAcronym();
}
}
如果未提供转换器,则可以根据给定的查找查询计算目标参考文档。
在这种情况下,将评估关联目标属性,如以下示例所示。DocumentPointer
@Document
class Book {
@Id
ObjectId id;
String title;
List<String> author;
@DocumentReference(lookup = "{ 'acronym' : ?#{acc} }") (1) (2)
Publisher publisher;
}
@Document
class Publisher {
@Id
ObjectId id;
String acronym; (1)
String name;
// ...
}
{
"_id" : 9a48e32,
"title" : "The Warded Man",
"author" : ["Peter V. Brett"],
"publisher" : {
"acc" : "DOC"
}
}
1 | 使用字段查询集合中的实体。acronym Publisher |
2 | 查找查询 (like ) 的字段值占位符用于形成参考文档。acc |
也可以使用 和 的组合对关系样式的一对多引用进行建模。
此方法允许链接类型,而无需将链接值存储在拥有文档中,而是存储在引用文档上,如下例所示。@ReadonlyProperty
@DocumentReference
@Document
class Book {
@Id
ObjectId id;
String title;
List<String> author;
ObjectId publisherId; (1)
}
@Document
class Publisher {
@Id
ObjectId id;
String acronym;
String name;
@ReadOnlyProperty (2)
@DocumentReference(lookup="{'publisherId':?#{#self._id} }") (3)
List<Book> books;
}
Book
公文{
"_id" : 9a48e32,
"title" : "The Warded Man",
"author" : ["Peter V. Brett"],
"publisherId" : 8cfb002
}
Publisher
公文{
"_id" : 8cfb002,
"acronym" : "DR",
"name" : "Del Rey"
}
1 | 通过在文档中存储来设置从 (reference) 到 (owner) 的链接。Book Publisher Publisher.id Book |
2 | 将包含引用的属性标记为只读。
这可以防止将对单个 s 的引用与文档一起存储。Book Publisher |
3 | 使用变量访问文档中的值,并在此检索中使用 matching .#self Publisher Books publisherId |
完成上述所有操作后,就可以对实体之间的各种关联进行建模。 请查看下面的非详尽示例列表,以了解可能的情况。
class Entity {
@DocumentReference
ReferencedObject ref;
}
// entity
{
"_id" : "8cfb002",
"ref" : "9a48e32" (1)
}
// referenced object
{
"_id" : "9a48e32" (1)
}
1 | MongoDB 简单类型可以直接使用,无需进一步配置。 |
class Entity {
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }") (1)
ReferencedObject ref;
}
// entity
{
"_id" : "8cfb002",
"ref" : "9a48e32" (1)
}
// referenced object
{
"_id" : "9a48e32"
}
1 | target 定义引用值本身。 |
refKey
class Entity {
@DocumentReference(lookup = "{ '_id' : '?#{refKey}' }") (1) (2)
private ReferencedObject ref;
}
@WritingConverter
class ToDocumentPointerConverter implements Converter<ReferencedObject, DocumentPointer<Document>> {
public DocumentPointer<Document> convert(ReferencedObject source) {
return () -> new Document("refKey", source.id); (1)
}
}
// entity
{
"_id" : "8cfb002",
"ref" : {
"refKey" : "9a48e32" (1)
}
}
// referenced object
{
"_id" : "9a48e32"
}
1 | 用于获取引用值的 key 必须是写入时使用的 key。 |
2 | refKey 是 的缩写。target.refKey |
class Entity {
@DocumentReference(lookup = "{ 'firstname' : '?#{fn}', 'lastname' : '?#{ln}' }") (1) (2)
ReferencedObject ref;
}
// entity
{
"_id" : "8cfb002",
"ref" : {
"fn" : "Josh", (1)
"ln" : "Long" (1)
}
}
// referenced object
{
"_id" : "9a48e32",
"firstname" : "Josh", (2)
"lastname" : "Long", (2)
}
1 | 根据查找查询读取/写入键 & 从/到链接文档。fn ln |
2 | 使用非 ID 字段查找目标文档。 |
class Entity {
@DocumentReference(lookup = "{ '_id' : '?#{id}' }", collection = "?#{collection}") (2)
private ReferencedObject ref;
}
@WritingConverter
class ToDocumentPointerConverter implements Converter<ReferencedObject, DocumentPointer<Document>> {
public DocumentPointer<Document> convert(ReferencedObject source) {
return () -> new Document("id", source.id) (1)
.append("collection", … ); (2)
}
}
// entity
{
"_id" : "8cfb002",
"ref" : {
"id" : "9a48e32", (1)
"collection" : "…" (2)
}
}
1 | 从参考文档读取/写入键,以便在查找查询中使用它们。_id |
2 | 可以使用其键从引用文档中读取集合名称。 |
我们知道在查找查询中使用各种 MongoDB 查询运算符很诱人,这很好。 但有几个方面需要考虑:
一些更一般的评论:
|