Polishing.

Reorder API methods, remove unused MongoPersistentProperty.isNullable method, reduce visibility where possible. Add Javadoc and tweak documentation wording.

Introduce DotPath utility to abstract dot path concatenation.

See #2803.
Original pull request: #896.
This commit is contained in:
Mark Paluch
2021-02-16 11:10:06 +01:00
parent bd985a6589
commit f3d6f405c9
20 changed files with 320 additions and 137 deletions

View File

@@ -1,6 +1,11 @@
[[new-features]]
= New & Noteworthy
[[new-features.3.2]]
== What's New in Spring Data MongoDB 3.2
* Support for <<embedded-entities,Embedded Types>> to unwrap nested objects into the parent `Document`.
[[new-features.3.1]]
== What's New in Spring Data MongoDB 3.1

View File

@@ -1,30 +1,33 @@
[[embedded-entities]]
== Embedded Types
Embedded entities are used to design value objects in your Java domain model whose properties are flattened out into the MongoDB Document.
Embedded entities are used to design value objects in your Java domain model whose properties are flattened out into the parent's MongoDB Document.
[[embedded-entities.mapping]]
=== Embedded Types Mapping
In the example below you see, that `User.name` is annotated with `@Embedded`.
The consequence of this is that all properties of `UserName` are folded into the `user` document.
Consider the following domain model where `User.name` is annotated with `@Embedded`.
The `@Embedded` annotation signals that all properties of `UserName` should be unwrapped into the `user` document that owns the `name` property.
.Sample Code of embedding objects
====
[source,java]
----
public class User {
class User {
@Id
private String userId;
@Id
String userId;
@Embedded(onEmpty = USE_NULL) <1>
UserName name;
}
public class UserName {
private String firstname;
private String lastname;
class UserName {
String firstname;
String lastname;
}
----
@@ -41,37 +44,42 @@ By using `onEmpty=USE_EMPTY` an empty `UserName`, with potential `null` value fo
====
For less verbose embeddable type declarations use `@Embedded.Nullable` and `@Embedded.Empty` instead `@Embedded(onEmpty = USE_NULL)` and `@Embedded(onEmpty = USE_EMPTY)`.
Using those annotations simultaneously set JSR-305 `@javax.annotation.Nonnull` accordingly.
Both annotations are meta-annotated with JSR-305 `@javax.annotation.Nonnull` to aid with nullability inspections.
[WARNING]
====
It is possible to use complex types within an embedded object.
However those must not be, nor contain embedded fields themselves.
However, those must not be, nor contain embedded fields themselves.
====
[[embedded-entities.mapping.field-names]]
=== Embedded Types field names
A value object can be embedded multiple times by using the optional `prefix` attribute of the `@Embedded` annotation.
By dosing so the chosen prefix is prepended to each property or `@Field("...")` name in the embedded object.
By dosing so the chosen prefix is prepended to each property or `@Field("")` name in the embedded object.
Please note that values will overwrite each other if multiple properties render to the same field name.
.Sample Code of embedded object with name prefix
====
[source,java]
----
public class User {
class User {
@Id
private String userId;
@Id
String userId;
@Embedded.Nullable(prefix = "u") <1>
@Embedded.Nullable(prefix = "u_") <1>
UserName name;
@Embedded.Nullable(prefix = "a_") <2>
UserName name;
}
public class UserName {
private String firstname;
private String lastname;
class UserName {
String firstname;
String lastname;
}
----
@@ -79,11 +87,14 @@ public class UserName {
----
{
"_id" : "a6a805bd-f95f",
"ufirstname" : "Jean",
"ulastname" : "Grey"
"u_firstname" : "Jean", <1>
"u_lastname" : "Grey",
"a_firstname" : "Something", <2>
"a_lastname" : "Else"
}
----
<1> The prefix `u` is prepended to all properties of `UserName`.
<1> All properties of `UserName` are prefixed with `u_`.
<2> All properties of `UserName` are prefixed with `a_`.
====
While combining the `@Field` annotation with `@Embedded` on the very same property does not make sense and therefore leads to an error.
@@ -104,7 +115,7 @@ public class User {
public class UserName {
@Field("first-name") <2>
@Field("first-name") <2>
private String firstname;
@Field("last-name")
@@ -116,18 +127,18 @@ public class UserName {
----
{
"_id" : "2647f7b9-89da",
"u-first-name" : "Barbara", <2>
"u-first-name" : "Barbara", <2>
"u-last-name" : "Gordon"
}
----
<1> The prefix `u-` is prepended to all properties of `UserName`.
<2> The field name is the result of the combination of the annotated field name an the chosen prefix.
<1> All properties of `UserName` are prefixed with `u-`.
<2> Final field names are a result of concatenating `@Embedded(prefix)` and `@Field(name)`.
====
[[embedded-entities.queries]]
=== Query on Embedded Objects
Defining queries on embedded properties is possible on type as well as field level as the provided `Critieria` is matched against the domain type.
Defining queries on embedded properties is possible on type- as well as field-level as the provided `Criteria` is matched against the domain type.
Prefixes and potential custom field names will be considered when rendering the actual query.
Use the property name of the embedded object to match against all contained fields as shown in the sample below.
@@ -149,7 +160,7 @@ db.collection.find({
----
====
It is also possible to address any field of the embedded object directly via its property name as shown in the snippet below.
It is also possible to address any field of the embedded object directly using its property name as shown in the snippet below.
.Query on field of embedded object
====
@@ -194,7 +205,7 @@ Though possible, using the embedded object itself as sort criteria includes all
====
[[embedded-entities.queries.project]]
==== Project on embedded object
==== Field projection on embedded objects
Fields of embedded objects can be subject for projection either as a whole or via single fields as shown in the samples below.
@@ -203,7 +214,7 @@ Fields of embedded objects can be subject for projection either as a whole or vi
[source,java]
----
Query findByUserLastName = query(where("name.firstname").is("Gamora"));
findByUserLastName.fields().include("name"); <1>
findByUserLastName.fields().include("name"); <1>
List<User> user = template.findAll(findByUserName, User.class);
----
@@ -225,7 +236,7 @@ db.collection.find({
[source,java]
----
Query findByUserLastName = query(where("name.lastname").is("Smoak"));
findByUserLastName.fields().include("name.firstname"); <1>
findByUserLastName.fields().include("name.firstname"); <1>
List<User> user = template.findAll(findByUserName, User.class);
----
@@ -258,9 +269,9 @@ The `Repository` abstraction allows deriving queries on fields of embedded objec
----
interface UserRepository extends CrudRepository<User, String> {
List<User> findByName(UserName username); <1>
List<User> findByName(UserName username); <1>
List<User> findByNameFirstname(String firstname); <1>
List<User> findByNameFirstname(String firstname); <2>
}
----
<1> Matches against all fields of the embedded object.
@@ -330,14 +341,14 @@ db.collection.update({
=== Aggregations on Embedded Objects
The <<mongo.aggregation,Aggregation Framework>> will attempt to map embedded values of typed aggregations.
Please make sure to work with the properties path including the embedded wrapper object when referencing one of it's values.
Please make sure to work with the property path including the embedded wrapper object when referencing one of its values.
Other than that no special action is required.
[[embedded-entities.indexes]]
=== Index on Embedded Objects
It is possible to attach the `@Indexed` annotation to properties of an embedded type just as it is done with regular objects.
However it is not possible to use `@Indexed` along with the `@Embedded` annotation on the very same property of an object.
It is not possible to use `@Indexed` along with the `@Embedded` annotation on the owning property.
====
[source,java]
@@ -348,9 +359,10 @@ public class User {
private String userId;
@Embedded(onEmpty = USE_NULL)
UserName name; <1>
UserName name; <1>
@Indexed <2> // Invalid -> InvalidDataAccessApiUsageException
// Invalid -> InvalidDataAccessApiUsageException
@Indexed <2>
@Embedded(onEmpty = USE_Empty)
Address address;
}
@@ -360,7 +372,7 @@ public class UserName {
private String firstname;
@Indexed
private String lastname; <1>
private String lastname; <1>
}
----
<1> Index created for `lastname` in `users` collection.