Skip to content

Commit c906a85

Browse files
Update documentation
1 parent 6509b9e commit c906a85

File tree

3 files changed

+266
-3
lines changed

3 files changed

+266
-3
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
public @interface DocumentReference {
3535

3636
/**
37-
* The database the referred entity resides in.
37+
* The database the linked entity resides in.
3838
*
3939
* @return empty String by default.
4040
*/

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
import java.util.Map;
3333

3434
import org.bson.Document;
35-
import org.junit.Ignore;
3635
import org.junit.jupiter.api.BeforeEach;
36+
import org.junit.jupiter.api.Disabled;
3737
import org.junit.jupiter.api.Test;
3838
import org.junit.jupiter.api.extension.ExtendWith;
3939
import org.springframework.core.convert.converter.Converter;
@@ -752,7 +752,7 @@ void updateReferenceCollectionWithValue() {
752752
}
753753

754754
@Test // GH-3602
755-
@Ignore("Property path resolution does not work inside maps, the key is considered :/")
755+
@Disabled("Property path resolution does not work inside maps, the key is considered :/")
756756
void updateReferenceMapWithEntity() {
757757

758758
String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);

src/main/asciidoc/reference/mapping.adoc

+263
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ The MappingMongoConverter can use metadata to drive the mapping of objects to do
480480
* `@MongoId`: Applied at the field level to mark the field used for identity purpose. Accepts an optional `FieldType` to customize id conversion.
481481
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the name of the collection where the data will be stored.
482482
* `@DBRef`: Applied at the field to indicate it is to be stored using a com.mongodb.DBRef.
483+
* `@DocumentReference`: Applied at the field to indicate it is to be stored as a pointer to another document. This can be a single value (the _id_ by default), or a `Document` provided via a converter.
483484
* `@Indexed`: Applied at the field level to describe how to index the field.
484485
* `@CompoundIndex` (repeatable): Applied at the type level to declare Compound Indexes.
485486
* `@GeoSpatialIndexed`: Applied at the field level to describe how to geoindex the field.
@@ -826,6 +827,268 @@ Required properties that are also defined as lazy loading ``DBRef`` and used as
826827
TIP: Lazily loaded ``DBRef``s can be hard to debug. Make sure tooling does not accidentally trigger proxy resolution by eg. calling `toString()` or some inline debug rendering invoking property getters.
827828
Please consider to enable _trace_ logging for `org.springframework.data.mongodb.core.convert.DefaultDbRefResolver` to gain insight on `DBRef` resolution.
828829

830+
[[mapping-usage.linking]]
831+
=== Using Document References
832+
833+
Using `@DocumentReference` offers an alternative way of linking entities in MongoDB.
834+
While the goal is the same as when using <<mapping-usage-references,DBRefs>>, the store representation is different.
835+
`DBRef` resolves to a document with a fixed structure as outlined in the https://docs.mongodb.com/manual/reference/database-references/[MongoDB Reference documentation]. +
836+
Document references, do not follow a specific format.
837+
They can be literally anything, a single value, an entire document, basically everything that can be stored in MongoDB.
838+
By default, the mapping layer will use the referenced entities _id_ value for storage and retrieval, like in the sample below.
839+
840+
====
841+
[source,java]
842+
----
843+
@Document
844+
public class Account {
845+
846+
@Id
847+
private String id;
848+
private Float total;
849+
}
850+
851+
@Document
852+
public class Person {
853+
854+
@Id
855+
private String id;
856+
857+
@DocumentReference <1>
858+
private List<Account> accounts;
859+
}
860+
----
861+
[source,java]
862+
----
863+
Account account = ...
864+
865+
tempate.insert(account); <2>
866+
867+
template.update(Person.class)
868+
.matching(where("id").is(...))
869+
.apply(new Update().push("accounts").value(account)) <3>
870+
.first();
871+
----
872+
[source,json]
873+
----
874+
{
875+
"_id" : ...,
876+
"accounts" : [ "6509b9e", ... ] <4>
877+
}
878+
----
879+
<1> Mark the collection of `Account` values to be linked.
880+
<2> The mapping framework does not handle cascading saves, so make sure to persist the referenced entity individually.
881+
<3> Add the reference to the existing entity.
882+
<4> Linked `Account` entities are represented as an array of their `_id` values.
883+
====
884+
885+
The sample above uses an `_id` based fetch query (`{ '_id' : ?#{#target} }`) for data retrieval and resolves linked entities eagerly.
886+
It is possible to alter resolution defaults (listed below) via the attributes of `@DocumentReference`
887+
888+
.@DocumentReference defaults
889+
[cols="2,3,5", options="header"]
890+
|===
891+
| Attribute | Description | Default
892+
893+
| `db`
894+
| The target database name for collection lookup.
895+
| The configured database provided by `MongoDatabaseFactory.getMongoDatabase()`.
896+
897+
| `collection`
898+
| The target collection name.
899+
| The annotated properties domain type, respectively the value type in case of `Collection` like or `Map` properties, collection name.
900+
901+
| `lookup`
902+
| The single document lookup query. `Collection` like or `Map` properties combine individual lookups via an `$or` operator.
903+
| An `_id` field based query (`{ '_id' : ?#{#target} }`) using the loaded source value.
904+
905+
| `lazy`
906+
| If set to `true` value resolution is delayed upon first access of the property.
907+
| Resolves properties eagerly by default.
908+
|===
909+
910+
`@DocumentReference(lookup=...)` allows to define custom queries that are independent from the `_id` field and therefore offer a flexible way of defining links between entities as demonstrated in the sample below, where the `Publisher` of a book is referenced by its acronym instead of the internal `id`.
911+
912+
====
913+
[source,java]
914+
----
915+
@Document
916+
public class Book {
917+
918+
@Id
919+
private ObjectId id;
920+
private String title;
921+
private List<String> author;
922+
923+
@Field("publisher_ac")
924+
@DocumentReference(lookup = "{ 'acronym' : ?#{#target} }") <1>
925+
private Publisher publisher;
926+
}
927+
928+
@Document
929+
public class Publisher {
930+
931+
@Id
932+
private ObjectId id;
933+
private String acronym; <1>
934+
private String name;
935+
936+
@DocumentReference(lazy = true) <2>
937+
private List<Book> books;
938+
939+
}
940+
----
941+
[source,json]
942+
----
943+
{
944+
"_id" : 9a48e32,
945+
"title" : "The Warded Man",
946+
"author" : ["Peter V. Brett"],
947+
"publisher_ac" : "DR"
948+
}
949+
----
950+
<1> Use the `acronym` field to query for entities in the `Publisher` collection.
951+
<2> Lazy load back references to the `Book` collection.
952+
====
953+
954+
The above snipped shows the reading side of things when working with custom linked objects.
955+
To make the writing part aware of the modified document pointer a custom converter, like the one below, needs to be registered.
956+
957+
====
958+
[source,java]
959+
----
960+
@WritingConverter
961+
class PublisherReferenceConverter implements Converter<Publisher, DocumentPointer<String>> {
962+
963+
@Override
964+
public DocumentPointer<Document> convert(Publisher source) {
965+
return () -> source.getAcronym();
966+
}
967+
}
968+
----
969+
====
970+
971+
With the above in place it is possible to model all kind of associations between entities.
972+
We listed samples of what is possible, with the according write converters in place, below.
973+
974+
.Simple Document Reference using _id_ field
975+
====
976+
[source,java]
977+
----
978+
@DocumentReference
979+
private ReferencedObject ref;
980+
----
981+
982+
[source,json]
983+
----
984+
// entity
985+
{
986+
"ref" : 9a48e32
987+
}
988+
989+
// linked entity
990+
{
991+
"_id" : 9a48e32
992+
}
993+
----
994+
====
995+
996+
.Simple Document Reference using _id_ field with explicit lookup query
997+
====
998+
[source,java]
999+
----
1000+
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }")
1001+
private ReferencedObject ref;
1002+
----
1003+
1004+
[source,json]
1005+
----
1006+
// entity
1007+
{
1008+
// ...
1009+
"ref" : 9a48e32
1010+
}
1011+
1012+
// linked entity
1013+
{
1014+
"_id" : 9a48e32
1015+
}
1016+
----
1017+
====
1018+
1019+
.Document Reference extracting field of linkage document for lookup query
1020+
====
1021+
[source,java]
1022+
----
1023+
1024+
@DocumentReference(lookup = "{ '_id' : '?#{refKey}' }")
1025+
private ReferencedObject ref;
1026+
----
1027+
1028+
[source,json]
1029+
----
1030+
// entity
1031+
{
1032+
"ref" : {
1033+
"refKey" : 9a48e32
1034+
}
1035+
}
1036+
1037+
// linked entity
1038+
{
1039+
"_id" : 9a48e32
1040+
}
1041+
----
1042+
====
1043+
1044+
.Document Reference with multiple values forming the lookup query
1045+
====
1046+
[source,java]
1047+
----
1048+
1049+
@DocumentReference(lookup = "{ 'firstname' : '?#{fn}', 'lastname' : '?#{ln}' }")
1050+
private ReferencedObject ref;
1051+
----
1052+
1053+
[source,json]
1054+
----
1055+
// entity
1056+
{
1057+
"ref" : {
1058+
"fn" : "Josh",
1059+
"ln" : "Long"
1060+
}
1061+
}
1062+
1063+
// linked entity
1064+
{
1065+
"_id" : 9a48e32,
1066+
"firsntame" : "Josh",
1067+
"lastname" : "Long",
1068+
}
1069+
----
1070+
====
1071+
1072+
.Document Reference reading target collection from linkage document
1073+
====
1074+
[source,java]
1075+
----
1076+
@DocumentReference(lookup = "{ '_id' : '?#{id}' }", collection = "?#{collection}")
1077+
private ReferencedObject ref;
1078+
----
1079+
[source,json]
1080+
----
1081+
// entity
1082+
{
1083+
"ref" : {
1084+
"id" : 9a48e32,
1085+
"collection" : "..."
1086+
}
1087+
}
1088+
1089+
----
1090+
====
1091+
8291092
[[mapping-usage-events]]
8301093
=== Mapping Framework Events
8311094

0 commit comments

Comments
 (0)