Skip to content

Commit 50d6e3b

Browse files
committed
Documented using the Pojos
JAVA-2472
1 parent 41e461d commit 50d6e3b

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

docs/reference/content/bson/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ The BSON library comprehensively supports [BSON](http://www.bsonspec.org), the d
1717
of BSON documents
1818
- [Codec and CodecRegistry]({{< relref "codecs.md" >}}): Documentation of the driver's `Codec` API, an abstraction for producing and
1919
consuming BSON document representations using the stream-based readers and writers
20+
- [POJOs({{< relref "pojos.md" >}}): Documentation of the driver's POJO support.
2021
- [Extended JSON]({{< relref "extended-json.md" >}}): Documentation of the driver's support for MongoDB Extended JSON

docs/reference/content/bson/pojos.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
+++
2+
date = "2017-04-10T14:27:51-04:00"
3+
title = "POJOs"
4+
[menu.main]
5+
parent = "BSON"
6+
weight = 45
7+
pre = "<i class='fa fa-star'></i>"
8+
+++
9+
10+
## POJOs - Plain Old Java Objects
11+
12+
The 3.5 release of the driver adds POJO support via the [`PojoCodec`]({{<apiref "org/bson/codecs/pojo/PojoCodec.html">}}), which allows for
13+
direct serialization of POJOs to and from BSON. Internally, each `PojoCodec` utilizes a
14+
[`ClassModel`]({{<apiref "org/bson/codecs/pojo/ClassModel.html">}}) instance to store metadata about how the POJO should be serialized.
15+
16+
A `ClassModel` for a POJO includes:
17+
18+
* The class of the POJO.
19+
* A new instance factory. Handling the creation of new instances of the POJO. By default it requires the POJO to have an empty constructor.
20+
* Field information, a list of [`FieldModel`]({{<apiref "org/bson/codecs/pojo/FieldModel.html">}}) instances that contain all the field metadata. By default this includes all non static and non transient fields.
21+
* An optional IdField. By default the `_id` or `id` field in the POJO.
22+
* Type data for the POJO and its fields to work around type erasure.
23+
* An optional discriminator value. The discriminator is the value used to represent the POJO class being stored.
24+
* An optional discriminator key. The document field name for the discriminator.
25+
* The use discriminator flag. This determines if the discriminator should be serialized. By default it is off.
26+
27+
Each `FieldModel` includes:
28+
29+
* The field name.
30+
* The document field name, which is the key for the value when serialized to BSON. By default it is the same as the field name.
31+
* Type data, to work around type erasure.
32+
* An optional `Codec` for the field. The codec allows for fine grained control over how the field is encoded and decoded.
33+
* A serialization checker. This checks if the value should be serialized. By default, `null` values are not serialized.
34+
* A field accessor. Used to access field values from the POJO instance.
35+
* Use discriminator flag, only used when serializing other POJOs. By default it is off. When on the `PojoCodecProvider` copies the
36+
`ClassModel` for the field's type and turns on the use discriminator flag. The corresponding `ClassModel` must be configured with a
37+
discriminator key and value.
38+
39+
ClassModels are built using the [`ClassModelBuilder`]({{<apiref "org/bson/codecs/pojo/ClassModelBuilder.html">}}) which can be accessed via
40+
the [`ClassModel.builder(clazz)`]({{<apiref "org/bson/codecs/pojo/ClassModel.html#builder-java.lang.Class-">}}) method. The builder
41+
initially uses reflection to create the required metadata.
42+
43+
`PojoCodec` instances are created by the [`PojoCodecProvider`]({{<apiref "org/bson/codecs/pojo/PojoCodecProvider.html">}}) which is a
44+
`CodecProvider`. CodecProviders are used by the `CodecRegistry` to find the correct `Codec` for any given class.
45+
46+
{{% note class="important" %}}
47+
By default all POJOs **must** include an empty, no arguments, constructor.
48+
49+
All fields in a POJO must have a [`Codec`]({{< relref "codecs.md" >}}) registered in the `CodecRegistry` so that their values can be
50+
encoded and decoded.
51+
{{% /note %}}
52+
53+
## POJO support
54+
55+
The entry point for POJO support is the `PojoCodecProvider`. New instances can be created via the
56+
[`PojoCodecProvider.builder()`]({{<apiref "org/bson/codecs/pojo/PojoCodecProvider.html#builder">}}) method. The `builder` allows users to
57+
register any combination of:
58+
59+
* Individual POJO classes.
60+
* Package names containing POJO classes.
61+
* `ClassModel` instances which allow fine grained control over how a POJO is encoded and decoded.
62+
63+
The `builder` also allows the user to register default [Conventions](#conventions) for any POJOs that are automatically mapped, either
64+
the individual POJO classes or POJOs found from registered packages. The `PojoCodecProvider` will lookup PojoCodecs and return the first
65+
that matches the POJO class:
66+
67+
* Registered ClassModels
68+
* Registered POJO classes
69+
* Registered POJO classes contained in one of the registered packages
70+
71+
Once the `PojoCodecProvider` has been built, by calling `builder.build()`, it can be combined with an existing `CodecRegistry` to create a
72+
new registry that will also support the registered POJOs. The following example registers the package `org.example.pojos` and creates a new
73+
`CodecRegistry`.
74+
75+
```java
76+
import org.bson.codecs.configuration.CodecProvider;
77+
import org.bson.codecs.configuration.CodecRegistry;
78+
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
79+
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
80+
81+
// Create a CodecRegistry containing the PojoCodecProvider instance.
82+
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register("org.example.pojos").build();
83+
CodecRegistry pojoCodecRegistry = fromRegistries(fromProviders(pojoCodecProvider), defaultCodecRegistry);
84+
```
85+
86+
{{% note class="tip" %}}
87+
In general only one instance of a `PojoCodecProvider` should be created.
88+
89+
This is because each `PojoCodecProvider` instance contains a look up table for discriminator names. If multiple PojoCodecProviders are
90+
used, care should be taken to ensure that each provider contains a holistic view of POJO classes, otherwise discriminator lookups can fail.
91+
Alternatively, using the full class name as the discriminator value will ensure successful POJO lookups.
92+
{{% /note %}}
93+
94+
## Default configuration
95+
96+
By default the `PojoCodec` will not store `null` values or a discriminator when converting a POJO to BSON.
97+
98+
Take the following `Person` class:
99+
100+
```java
101+
public class Person {
102+
private String firstName;
103+
private String lastName;
104+
private Address address = null;
105+
106+
public Person() { }
107+
108+
public Person(final String firstName, final String lastName) { }
109+
110+
// Rest of implementation
111+
112+
}
113+
```
114+
115+
The instance of `new Person("Ada", "Lovelace");` would be serialized to the equivalent of `{ firstName: "Ada", lastName: "Lovelace"}`.
116+
117+
Notice the `address` field is omitted because it hasn't been set and has a `null` value. If the person instance contained an address, it
118+
would be stored as a sub document and use the `CodecRegistry` to look up the `Codec` for the `Address` class and use that to
119+
encode and decode the address value.
120+
121+
### Generics support
122+
123+
Generics are fully supported. During the creation of a `ClassModelBuilder` type parameters are inspected and saved to work around type
124+
erasure. The only requirement is the top level POJO **cannot** contain any type parameters.
125+
126+
Take the following classes:
127+
128+
```java
129+
public class GenericClass<T> {
130+
T genericField;
131+
// Rest of implementation
132+
}
133+
134+
public class GenericTree<A, B> {
135+
GenericTree<A, B> left;
136+
GenericTree<A, B> right;
137+
// Rest of implementation
138+
}
139+
140+
public final class Tree extends GenericTree<Integer, String> {
141+
GenericClass<Long> genericClass;
142+
// Rest of implementation
143+
}
144+
```
145+
146+
The `Tree` POJO is serializable because it doesn't have any unknown type parameters. The `left`, `right` and `genericClass` fields are all
147+
serializable because they are bound to the concrete types `Integer`, `String` and `Long`.
148+
149+
On their own, instances of `GenericTree` or `GenericClass` are not serializable by the `PojoCodec`. This is because the runtime type parameter
150+
information is erased by the JVM, and the type parameters cannot be specialized accurately.
151+
152+
### Conventions
153+
154+
The [`Convention`]({{<apiref "org/bson/codecs/pojo/Convention.html">}}) interface provides a mechanism for `ClassModelBuilder`
155+
instances to be configured during the build stage and the creation of the `ClassModel`.
156+
157+
The following Conventions are available from the [`Conventions`]({{<apiref "org/bson/codecs/pojo/Conventions.html">}}) class:
158+
159+
* The [`ANNOTATION_CONVENTION`]({{<apiref "org/bson/codecs/pojo/Conventions.html#ANNOTATION_CONVENTION">}}). Applies all the
160+
default annotations.
161+
* The [`CLASS_AND_FIELD_CONVENTION`]({{<apiref "org/bson/codecs/pojo/Conventions.html#CLASS_AND_FIELD_CONVENTION">}}). Sets the
162+
discriminator key if not set to `_t` and the discriminator value if not set to the ClassModels simple type name. Also, configures
163+
the FieldModels. Sets the document field name if not set to the field name. If the `idField` isn't set and there is a field named
164+
`_id` or `id` then it will be marked as the `idField`.
165+
* The [`DEFAULT_CONVENTIONS`]({{<apiref "org/bson/codecs/pojo/Conventions.html#DEFAULT_CONVENTIONS">}}), a list containing the
166+
`ANNOTATION_CONVENTION` and the `CLASS_AND_FIELD_CONVENTION`.
167+
* The [`NO_CONVENTIONS`]({{<apiref "org/bson/codecs/pojo/Conventions.html#NO_CONVENTIONS">}}) an empty list.
168+
169+
Custom Conventions can either be set globally via the
170+
[`PojoCodecProvider.Builder.conventions(conventions)`]({{<apiref "org/bson/codecs/pojo/PojoCodecProvider.Builder.html#conventions-java.util.List-">}})
171+
method, or via the [`ClassModelBuilder.conventions(conventions)`]({{<apiref "org/bson/codecs/pojo/ClassModelBuilder.html#conventions-java.util.List-">}})
172+
method.
173+
174+
{{% note class="note" %}}
175+
Conventions are applied in order during the build stage when creating a `ClassModel`.
176+
177+
Each `Convention` can mutate the underlying `ClassModelBuilder`, so care should be taken that Conventions do not conflict with each other
178+
in their intent.
179+
{{% /note %}}
180+
181+
182+
### Annotations
183+
184+
Annotations require the `ANNOTATION_CONVENTION` and provide an easy way to configure how POJOs are serialized.
185+
186+
The following annotations are available from the
187+
[`org.bson.codecs.pojo.annotations`]({{<apiref "org/bson/codecs/pojo/annotations/package-summary.html">}}) package:
188+
189+
* [`Discriminator`]({{<apiref "org/bson/codecs/pojo/annotations/Discriminator.html">}}), enables using a discriminator.
190+
Also allows for setting a custom discriminator key and value.
191+
* [`Id`]({{<apiref "org/bson/codecs/pojo/annotations/Id.html">}}), marks a field to be serialized as the `_id` field.
192+
* [`Property`]({{<apiref "org/bson/codecs/pojo/annotations/Property.html">}}). Allows for an alternative document field
193+
name when converting the POJO field to BSON. Also, allows a field to turn on using a discriminator when storing a POJO value.
194+
195+
Take the following `Person` class:
196+
197+
```java
198+
import org.bson.codecs.pojo.annotations.*;
199+
200+
@Discriminator
201+
public class Person {
202+
@Id
203+
private String personId;
204+
private String firstName;
205+
private String lastName;
206+
@Property(useDiscriminator = true)
207+
private Address address;
208+
209+
// Rest of implementation
210+
}
211+
```
212+
213+
Will produce BSON similar to:
214+
215+
```json
216+
{ "_id": "1234567890", "_t": "Person", "firstName": "Alan", "lastName": "Turing",
217+
"address": { "_t": "Address", "address": "The Mansion", "street": "Sherwood Drive",
218+
"town": "Bletchley", "postcode": "MK3 6EB" } }
219+
```
220+
221+
The `_id` field maps to the POJO's `personId` field. The `_t` field contains the discriminator and the `address` field also contains a
222+
discriminator.
223+
224+
225+
## Advanced configuration
226+
227+
For most scenarios there is no need for further configuration. However, there are some scenarios where custom configuration is required.
228+
229+
### Fields with abstract or interface types.
230+
231+
If a POJO contains a field that has an abstract type or has an interface as its type, then a discriminator is required. The type and all
232+
subtypes / implementations need to be registered with the `PojoCodecProvider` so that values can be encoded and decoded correctly.
233+
234+
The easiest way to enable a discriminator is to annotate the abstract class with the `Discriminator` annotation. Alternatively, the
235+
[`ClassModelBuilder.enableDiscriminator(true)`]({{<apiref "org/bson/codecs/pojo/ClassModelBuilder.html#enableDiscriminator-boolean-">}})
236+
method can be used to enable the use of a discriminator.
237+
238+
The following example creates a `CodecRegistry` with discriminators enabled for a `User` interface and its concrete `FreeUser` and
239+
`SubscriberUser` implementations:
240+
241+
```java
242+
ClassModel<User> userModel = ClassModel.builder(User.class).enableDiscriminator(true).build();
243+
ClassModel<FreeUser> freeUserModel = ClassModel.builder(FreeUser.class).enableDiscriminator(true).build();
244+
ClassModel<SubscriberUser> subscriberUserModel = ClassModel.builder(SubscriberUser.class).enableDiscriminator(true).build();
245+
246+
PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(userModel, freeUserModel, subscriberUserModel).build();
247+
248+
CodecRegistry pojoCodecRegistry = fromRegistries(fromProviders(pojoCodecProvider), defaultCodecRegistry);
249+
```
250+
251+
### Supporting POJOs without no args constructors
252+
253+
By default PojoCodecs only work with POJOs that have an empty, no arguments, constructor. POJOs with alternative constructors can be
254+
supported but require a custom implementation of the [`InstanceCreatorFactory`]({{<apiref "org/bson/codecs/pojo/InstanceCreatorFactory.html">}}),
255+
which can be set on the `ClassModelBuilder`.
256+
257+
258+
### Changing what is serialized
259+
260+
By default `null` values aren't serialized. This is controlled by the default implementation of the
261+
[`FieldSerialization`]({{<apiref "org/bson/codecs/pojo/FieldSerialization.html">}}) interface. Custom implementations can be set on
262+
the `FieldModelBuilder` which is available from the `ClassModelBuilder`.
263+

0 commit comments

Comments
 (0)