Skip to content

Commit 6c78727

Browse files
Update Documentation
1 parent c906a85 commit 6c78727

File tree

6 files changed

+242
-46
lines changed

6 files changed

+242
-46
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -519,13 +519,15 @@ private void readAssociation(Association<MongoPersistentProperty> association, P
519519

520520
if (conversionService.canConvert(DocumentPointer.class, property.getActualType())) {
521521

522-
// collection like special treatment
523-
accessor.setProperty(property, conversionService.convert(new DocumentPointer() {
522+
DocumentPointer<?> pointer = new DocumentPointer<Object>() {
524523
@Override
525524
public Object getPointer() {
526525
return value;
527526
}
528-
}, property.getActualType()));
527+
};
528+
529+
// collection like special treatment
530+
accessor.setProperty(property, conversionService.convert(pointer, property.getActualType()));
529531
} else {
530532
accessor.setProperty(property,
531533
dbRefResolver.resolveReference(property, value, referenceReader, context::convert));

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/DocumentPointer.java

+10
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@
1616
package org.springframework.data.mongodb.core.mapping;
1717

1818
/**
19+
* A custom pointer to a linked document to be used along with {@link DocumentReference} for storing the linkage value.
20+
*
1921
* @author Christoph Strobl
2022
*/
2123
@FunctionalInterface
2224
public interface DocumentPointer<T> {
25+
26+
/**
27+
* The actual pointer value. This can be any simple type, like a {@link String} or {@link org.bson.types.ObjectId} or
28+
* a {@link org.bson.Document} holding more information like the target collection, multiple fields forming the key,
29+
* etc.
30+
*
31+
* @return the value stored in MongoDB and used for constructing the {@link DocumentReference#lookup() lookup query}.
32+
*/
2333
T getPointer();
2434
}

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/DocumentReference.java

+83-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,69 @@
2424
import org.springframework.data.annotation.Reference;
2525

2626
/**
27+
* A {@link DocumentReference} offers an alternative way of linking entities in MongoDB. While the goal is the same as
28+
* when using {@link DBRef}, the store representation is different and can be literally anything, a single value, an
29+
* entire {@link org.bson.Document}, basically everything that can be stored in MongoDB. By default, the mapping layer
30+
* will use the referenced entities {@literal id} value for storage and retrieval.
31+
*
32+
* <pre class="code">
33+
* public class Account {
34+
* private String id;
35+
* private Float total;
36+
* }
37+
*
38+
* public class Person {
39+
* private String id;
40+
* &#64;DocumentReference
41+
* private List&lt;Account&gt; accounts;
42+
* }
43+
*
44+
* Account account = ...
45+
*
46+
* mongoTemplate.insert(account);
47+
*
48+
* template.update(Person.class)
49+
* .matching(where("id").is(...))
50+
* .apply(new Update().push("accounts").value(account))
51+
* .first();
52+
* </pre>
53+
*
54+
* {@link #lookup()} allows to define custom queries that are independent from the {@literal id} field and in
55+
* combination with {@link org.springframework.data.convert.WritingConverter writing converters} offer a flexible way of
56+
* defining links between entities.
57+
*
58+
* <pre class="code">
59+
* public class Book {
60+
* private ObjectId id;
61+
* private String title;
62+
*
63+
* &#64;Field("publisher_ac")
64+
* &#64;DocumentReference(lookup = "{ 'acronym' : ?#{#target} }")
65+
* private Publisher publisher;
66+
* }
67+
*
68+
* public class Publisher {
69+
*
70+
* private ObjectId id;
71+
* private String acronym;
72+
* private String name;
73+
*
74+
* &#64;DocumentReference(lazy = true)
75+
* private List&lt;Book&gt; books;
76+
* }
77+
*
78+
* &#64;WritingConverter
79+
* public class PublisherReferenceConverter implements Converter&lt;Publisher, DocumentPointer&lt;String&gt;&gt; {
80+
*
81+
* public DocumentPointer&lt;String&gt; convert(Publisher source) {
82+
* return () -> source.getAcronym();
83+
* }
84+
* }
85+
* </pre>
86+
*
2787
* @author Christoph Strobl
2888
* @since 3.3
89+
* @see <a href="https://docs.mongodb.com/manual/reference/database-references/#std-label-document-references">MongoDB Reference Documentation</a>
2990
*/
3091
@Documented
3192
@Retention(RetentionPolicy.RUNTIME)
@@ -36,15 +97,36 @@
3697
/**
3798
* The database the linked entity resides in.
3899
*
39-
* @return empty String by default.
100+
* @return empty String by default. Uses the default database provided buy the {@link org.springframework.data.mongodb.MongoDatabaseFactory}.
40101
*/
41102
String db() default "";
42103

104+
/**
105+
* The database the linked entity resides in.
106+
*
107+
* @return empty String by default. Uses the property type for collection resolution.
108+
*/
43109
String collection() default "";
44110

111+
/**
112+
* The single document lookup query. In case of an {@link java.util.Collection} or {@link java.util.Map} property
113+
* the individual lookups are combined via an `$or` operator.
114+
*
115+
* @return an {@literal _id} based lookup.
116+
*/
45117
String lookup() default "{ '_id' : ?#{#target} }";
46118

119+
/**
120+
* A specific sort.
121+
*
122+
* @return empty String by default.
123+
*/
47124
String sort() default "";
48125

126+
/**
127+
* Controls whether the referenced entity should be loaded lazily. This defaults to {@literal false}.
128+
*
129+
* @return {@literal false} by default.
130+
*/
49131
boolean lazy() default false;
50132
}

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java

+42-11
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.junit.jupiter.api.extension.ExtendWith;
3939
import org.springframework.core.convert.converter.Converter;
4040
import org.springframework.data.annotation.Id;
41+
import org.springframework.data.annotation.PersistenceConstructor;
4142
import org.springframework.data.convert.WritingConverter;
4243
import org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils;
4344
import org.springframework.data.mongodb.core.mapping.DocumentPointer;
@@ -73,7 +74,8 @@ public class MongoTemplateDocumentReferenceTests {
7374
});
7475

7576
cfg.configureConversion(it -> {
76-
it.customConverters(new ReferencableConverter());
77+
it.customConverters(new ReferencableConverter(), new SimpleObjectRefWithReadingConverterToDocumentConverter(),
78+
new DocumentToSimpleObjectRefWithReadingConverter());
7779
});
7880

7981
cfg.configureMappingContext(it -> {
@@ -793,6 +795,28 @@ void updateReferenceMapWithValue() {
793795
assertThat(target).containsEntry("mapValueRef", new Document("beastie", "ref-1").append("rise", "ref-2"));
794796
}
795797

798+
@Test // GH-3602
799+
void useReadingWriterConverterPairForLoading() {
800+
801+
SingleRefRoot root = new SingleRefRoot();
802+
root.id = "root-1";
803+
root.withReadingConverter = new SimpleObjectRefWithReadingConverter("ref-1", "value-1");
804+
805+
template.save(root.withReadingConverter);
806+
807+
template.save(root);
808+
809+
Document target = template.execute(db -> {
810+
return db.getCollection(template.getCollectionName(SingleRefRoot.class))
811+
.find(Filters.eq("_id", root.id)).first();
812+
});
813+
814+
assertThat(target).containsEntry("withReadingConverter", new Document("ref-key-from-custom-write-converter", root.withReadingConverter.id));
815+
816+
SingleRefRoot loaded = template.findOne(query(where("id").is(root.id)), SingleRefRoot.class);
817+
assertThat(loaded.withReadingConverter).isInstanceOf(SimpleObjectRefWithReadingConverter.class);
818+
}
819+
796820
@Data
797821
static class SingleRefRoot {
798822

@@ -868,9 +892,10 @@ static class SimpleObjectRef {
868892
@Setter
869893
static class SimpleObjectRefWithReadingConverter extends SimpleObjectRef {
870894

871-
public SimpleObjectRefWithReadingConverter(String id, String value, String id1, String value1) {
895+
public SimpleObjectRefWithReadingConverter(String id, String value) {
872896
super(id, value);
873897
}
898+
874899
}
875900

876901
@Data
@@ -927,17 +952,12 @@ public DocumentPointer convert(ReferenceAble source) {
927952
class DocumentToSimpleObjectRefWithReadingConverter
928953
implements Converter<DocumentPointer<Document>, SimpleObjectRefWithReadingConverter> {
929954

930-
private final MongoTemplate template;
931-
932-
public DocumentToSimpleObjectRefWithReadingConverter(MongoTemplate template) {
933-
this.template = template;
934-
}
935-
936955
@Nullable
937956
@Override
938957
public SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> source) {
939-
return template.findOne(query(where("id").is(source.getPointer().get("the-ref-key-you-did-not-expect"))),
940-
SimpleObjectRefWithReadingConverter.class);
958+
959+
Document document = client.getDatabase(DB_NAME).getCollection("simple-object-ref").find(Filters.eq("_id", source.getPointer().get("ref-key-from-custom-write-converter"))).first();
960+
return new SimpleObjectRefWithReadingConverter(document.getString("_id"), document.getString("value"));
941961
}
942962
}
943963

@@ -948,7 +968,7 @@ class SimpleObjectRefWithReadingConverterToDocumentConverter
948968
@Nullable
949969
@Override
950970
public DocumentPointer<Document> convert(SimpleObjectRefWithReadingConverter source) {
951-
return () -> new Document("the-ref-key-you-did-not-expect", source.getId());
971+
return () -> new Document("ref-key-from-custom-write-converter", source.getId());
952972
}
953973
}
954974

@@ -980,4 +1000,15 @@ public Object toReference() {
9801000
return id;
9811001
}
9821002
}
1003+
1004+
static class ReferencedObject {}
1005+
1006+
class ToDocumentPointerConverter implements Converter<ReferencedObject, DocumentPointer<Document>> {
1007+
1008+
@Nullable
1009+
@Override
1010+
public DocumentPointer<Document> convert(ReferencedObject source) {
1011+
return () -> new Document("", source);
1012+
}
1013+
}
9831014
}

Diff for: src/main/asciidoc/new-features.adoc

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
[[new-features]]
22
= New & Noteworthy
33

4+
[[new-features.3.3]]
5+
== What's New in Spring Data MongoDB 3.3
6+
7+
* Extended support for <<mapping-usage.linking, linking>> entities.
8+
49
[[new-features.3.2]]
510
== What's New in Spring Data MongoDB 3.2
611

0 commit comments

Comments
 (0)