Skip to content

Commit bca7e48

Browse files
Update Documentation
1 parent 4c58bd0 commit bca7e48

File tree

2 files changed

+372
-0
lines changed

2 files changed

+372
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
[[embedded-entities]]
2+
== Embedded Types
3+
4+
Embedded entities are used to design value objects in your Java domain model whose properties are flattened out into the MongoDB Document.
5+
6+
[[embedded-entities.mapping]]
7+
=== Embedded Types Mapping
8+
9+
In the example below you see, that `User.name` is annotated with `@Embedded`.
10+
The consequence of this is that all properties of `UserName` are folded into the `user` document.
11+
12+
.Sample Code of embedding objects
13+
====
14+
[source,java]
15+
----
16+
public class User {
17+
18+
@Id
19+
private String userId;
20+
21+
@Embedded(onEmpty = USE_NULL) <1>
22+
UserName name;
23+
}
24+
25+
public class UserName {
26+
private String firstname;
27+
private String lastname;
28+
}
29+
----
30+
31+
[source,json]
32+
----
33+
{
34+
"_id" : "1da2ba06-3ba7",
35+
"firstname" : "Emma",
36+
"lastname" : "Frost"
37+
}
38+
----
39+
<1> When loading the `name` property its value is set to `null` if both `firstname` and `lastname` are either `null` or not present.
40+
By using `onEmpty=USE_EMPTY` an empty `UserName`, with potential `null` value for its properties, will be created.
41+
====
42+
43+
For less verbose embeddable type declarations use `@Embedded.Nullable` and `@Embedded.Empty` instead `@Embedded(onEmpty = USE_NULL)` and `@Embedded(onEmpty = USE_EMPTY)`.
44+
Using those annotations simultaneously set JSR-305 `@javax.annotation.Nonnull` accordingly.
45+
46+
[WARNING]
47+
====
48+
It is possible to use complex types within an embedded object.
49+
However those must not be, nor contain embedded fields themselves.
50+
====
51+
52+
[[embedded-entities.mapping.field-names]]
53+
=== Embedded Types field names
54+
55+
A value object can be embedded multiple times by using the optional `prefix` attribute of the `@Embedded` annotation.
56+
By dosing so the chosen prefix is prepended to each property or `@Field("...")` name in the embedded object.
57+
Please note that values will overwrite each other if multiple properties render to the same field name.
58+
59+
.Sample Code of embedded object with name prefix
60+
====
61+
[source,java]
62+
----
63+
public class User {
64+
65+
@Id
66+
private String userId;
67+
68+
@Embedded.Nullable(prefix = "u") <1>
69+
UserName name;
70+
}
71+
72+
public class UserName {
73+
private String firstname;
74+
private String lastname;
75+
}
76+
----
77+
78+
[source,json]
79+
----
80+
{
81+
"_id" : "a6a805bd-f95f",
82+
"ufirstname" : "Jean",
83+
"ulastname" : "Grey"
84+
}
85+
----
86+
<1> The prefix `u` is prepended to all properties of `UserName`.
87+
====
88+
89+
While combining the `@Field` annotation with `@Embedded` on the very same property does not make sense and therefore leads to an error.
90+
It is a totally valid approach to use `@Field` on any of the embedded types properties.
91+
92+
.Sample Code embedded object with `@Field` annotation
93+
====
94+
[source,java]
95+
----
96+
public class User {
97+
98+
@Id
99+
private String userId;
100+
101+
@Embedded.Nullable(prefix = "u-") <1>
102+
UserName name;
103+
}
104+
105+
public class UserName {
106+
107+
@Field("first-name") <2>
108+
private String firstname;
109+
110+
@Field("last-name")
111+
private String lastname;
112+
}
113+
----
114+
115+
[source,json]
116+
----
117+
{
118+
"_id" : "2647f7b9-89da",
119+
"u-first-name" : "Barbara", <2>
120+
"u-last-name" : "Gordon"
121+
}
122+
----
123+
<1> The prefix `u-` is prepended to all properties of `UserName`.
124+
<2> The field name is the result of the combination of the annotated field name an the chosen prefix.
125+
====
126+
127+
[[embedded-entities.queries]]
128+
=== Query on Embedded Objects
129+
130+
Defining queries on embedded properties is possible on type as well as field level as the provided `Critieria` is matched against the domain type.
131+
Prefixes and potential custom field names will be considered when rendering the actual query.
132+
Use the property name of the embedded object to match against all contained fields as shown in the sample below.
133+
134+
.Query on embedded object
135+
====
136+
[source,java]
137+
----
138+
UserName userName = new UserName("Carol", "Danvers")
139+
Query findByUserName = query(where("name").is(userName));
140+
User user = template.findOne(findByUserName, User.class);
141+
----
142+
143+
[source,json]
144+
----
145+
db.collection.find({
146+
"firstname" : "Carol",
147+
"lastname" : "Danvers"
148+
})
149+
----
150+
====
151+
152+
It is also possible to address any field of the embedded object directly via its property name as shown in the snippet below.
153+
154+
.Query on field of embedded object
155+
====
156+
[source,java]
157+
----
158+
Query findByUserFirstName = query(where("name.firstname").is("Shuri"));
159+
List<User> users = template.findAll(findByUserFirstName, User.class);
160+
----
161+
162+
[source,json]
163+
----
164+
db.collection.find({
165+
"firstname" : "Shuri"
166+
})
167+
----
168+
====
169+
170+
[[embedded-entities.queries.sort]]
171+
==== Sort by embedded field.
172+
173+
Fields of embedded objects can be used for sorting via their property path as shown in the sample below.
174+
175+
.Sort on embedded field
176+
====
177+
[source,java]
178+
----
179+
Query findByUserLastName = query(where("name.lastname").is("Romanoff"));
180+
List<User> user = template.findAll(findByUserName.withSort(Sort.by("name.firstname")), User.class);
181+
----
182+
183+
[source,json]
184+
----
185+
db.collection.find({
186+
"lastname" : "Romanoff"
187+
}).sort({ "firstname" : 1 })
188+
----
189+
====
190+
191+
[NOTE]
192+
====
193+
Though possible, using the embedded object itself as sort criteria includes all of its fields in unpredictable order and may result in inaccurate ordering.
194+
====
195+
196+
[[embedded-entities.queries.project]]
197+
==== Project on embedded object
198+
199+
Fields of embedded objects can be subject for projection either as a whole or via single fields as shown in the samples below.
200+
201+
.Project on embedded object.
202+
====
203+
[source,java]
204+
----
205+
Query findByUserLastName = query(where("name.firstname").is("Gamora"));
206+
findByUserLastName.fields().include("name"); <1>
207+
List<User> user = template.findAll(findByUserName, User.class);
208+
----
209+
210+
[source,json]
211+
----
212+
db.collection.find({
213+
"lastname" : "Gamora"
214+
},
215+
{
216+
"firstname" : 1,
217+
"lastname" : 1
218+
})
219+
----
220+
<1> A field projection on an embedded object includes all of its properties.
221+
====
222+
223+
.Project on a field of an embedded object.
224+
====
225+
[source,java]
226+
----
227+
Query findByUserLastName = query(where("name.lastname").is("Smoak"));
228+
findByUserLastName.fields().include("name.firstname"); <1>
229+
List<User> user = template.findAll(findByUserName, User.class);
230+
----
231+
232+
[source,json]
233+
----
234+
db.collection.find({
235+
"lastname" : "Smoak"
236+
},
237+
{
238+
"firstname" : 1
239+
})
240+
----
241+
<1> A field projection on an embedded object includes all of its properties.
242+
====
243+
244+
[[embedded-entities.queries.by-example]]
245+
==== Query By Example on embedded object.
246+
247+
Embedded objects can be used within an `Example` probe just as any other type.
248+
Please review the <<query-by-example.running,Query By Example>> section, to learn more about this feature.
249+
250+
[[embedded-entities.queries.repository]]
251+
==== Repository Queries on embedded objects.
252+
253+
The `Repository` abstraction allows deriving queries on fields of embedded objects as well as the entire object.
254+
255+
.Repository queries on embedded objects.
256+
====
257+
[source,java]
258+
----
259+
interface UserRepository extends CrudRepository<User, String> {
260+
261+
List<User> findByName(UserName username); <1>
262+
263+
List<User> findByNameFirstname(String firstname); <1>
264+
}
265+
----
266+
<1> Matches against all fields of the embedded object.
267+
<2> Matches against the `firstname`.
268+
====
269+
270+
[NOTE]
271+
====
272+
Index creation for embedded objects is suspended even if the repository `create-query-indexes` namespace attribute is set to `true`.
273+
====
274+
275+
[[embedded-entities.update]]
276+
=== Update on Embedded Objects
277+
278+
Embedded objects can be updated as any other object that is part of the domain model.
279+
The mapping layer takes care of flattening embedded structures into their surroundings.
280+
It is possible to update single attributes of the embedded object as well as the entire value as shown in the examples below.
281+
282+
.Update a single field of an embedded object.
283+
====
284+
[source,java]
285+
----
286+
Update update = new Update().set("name.firstname", "Janet");
287+
template.update(User.class).matching(where("id").is("Wasp"))
288+
.apply(update).first()
289+
----
290+
291+
[source,json]
292+
----
293+
db.collection.update({
294+
"_id" : "Wasp"
295+
},
296+
{
297+
"$set" { "firstname" : "Janet" }
298+
},
299+
{ ... }
300+
)
301+
----
302+
====
303+
304+
.Update an embedded object.
305+
====
306+
[source,java]
307+
----
308+
Update update = new Update().set("name", new Name("Janet", "van Dyne"));
309+
template.update(User.class).matching(where("id").is("Wasp"))
310+
.apply(update).first()
311+
----
312+
313+
[source,json]
314+
----
315+
db.collection.update({
316+
"_id" : "Wasp"
317+
},
318+
{
319+
"$set" {
320+
"firstname" : "Janet",
321+
"lastname" : "van Dyne",
322+
}
323+
},
324+
{ ... }
325+
)
326+
----
327+
====
328+
329+
[[embedded-entities.aggregations]]
330+
=== Aggregations on Embedded Objects
331+
332+
The <<mongo.aggregation,Aggregation Framework>> will attempt to map embedded values of typed aggregations.
333+
Please make sure to work with the properties path including the embedded wrapper object when referencing one of it's values.
334+
Other than that no special action is required.
335+
336+
[[embedded-entities.indexes]]
337+
=== Index on Embedded Objects
338+
339+
It is possible to attach the `@Indexed` annotation to properties of an embedded type just as it is done with regular objects.
340+
However it is not possible to use `@Indexed` along with the `@Embedded` annotation on the very same property of an object.
341+
342+
====
343+
[source,java]
344+
----
345+
public class User {
346+
347+
@Id
348+
private String userId;
349+
350+
@Embedded(onEmpty = USE_NULL)
351+
UserName name; <1>
352+
353+
@Indexed <2> // Invalid -> InvalidDataAccessApiUsageException
354+
@Embedded(onEmpty = USE_Empty)
355+
Address address;
356+
}
357+
358+
public class UserName {
359+
360+
private String firstname;
361+
362+
@Indexed
363+
private String lastname; <1>
364+
}
365+
----
366+
<1> Index created for `lastname` in `users` collection.
367+
<2> Invalid `@Indexed` usage along with `@Embedded`
368+
====
369+
370+

src/main/asciidoc/reference/mapping.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -833,4 +833,6 @@ Events are fired throughout the lifecycle of the mapping process. This is descri
833833

834834
Declaring these beans in your Spring ApplicationContext causes them to be invoked whenever the event is dispatched.
835835

836+
include::embedded-documents.adoc[]
837+
836838
include::mongo-custom-conversions.adoc[]

0 commit comments

Comments
 (0)