Copyright © 2016-2019
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
1. Introduction
See the Datastore documentation for further information about the Datastore API .
|
The MongoDB Datastore
implementation uses the MongoDB Java Driver to perform data access and management operations on MongoDB databases.
1.1. Sources and contributions
The Holon Platform MongoDB Datastore module source code is available from the GitHub repository https://github.com/holon-platform/holon-datastore-mongo.
See the repository README
file for information about:
-
The source code structure.
-
How to build the module artifacts from sources.
-
Where to find the code examples.
-
How to contribute to the module development.
2. Obtaining the artifacts
The Holon Platform uses Maven for projects build and configuration. All the platform artifacts are published in the Maven Central Repository, so there is no need to explicitly declare additional repositories in your project pom
file.
At the top of each section of this documentation you will find the Maven coordinates (group id, artifact id and version) to obtain the artifact(s) as a dependency for your project.
A BOM (Bill Of Materials) pom
is provided to import the available dependencies for a specific version in your projects. The Maven coordinates for the core BOM are the following:
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-bom</artifactId>
<version>5.5.0</version>
The BOM can be imported in a Maven project in the following way:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-bom</artifactId>
<version>5.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.1. Using the Platform BOM
The Holon Platform provides an overall Maven BOM (Bill of Materials) to easily obtain all the available platform artifacts.
See Obtain the platform artifacts for details.
3. MongoDB Datastore
The MongoDB Datastore API is available in three implementations:
-
A synchronous implementation, which is the reference implementation of the Holon Platform Datastore API: See Synchronous MongoDB Datastore.
-
An asynchronous implementation, which is the reference implementation of the Holon Platform AsyncDatastore API: See Asynchronous MongoDB Datastore.
-
An reactive implementation, which is the reference implementation of the Holon Platform ReactiveDatastore API: See Reactive MongoDB Datastore.
3.1. MongoDB Datastore configuration
Regardless of the actual implementation, each MongoDB Datastore provides a set of configuration options, described below.
3.1.1. Database name
A MongoDB Datastore is always bound to a single MongoDB database, so the the database name configuration is required to build a MongoDB Datastore instance.
Each builder provides a database(String database)
method to set the database name to which the MongoDB Datastore is bound.
Example:
MongoDatastore datastore = MongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.database("my_db") (3)
.build();
1 | Obtain a builder to configure and create a new MongoDB Datastore (in this example, a synchronous MongoDatastore implementation) |
2 | Set the MongoDB client to use |
3 | Set the database name to use |
3.1.2. Common MongoDB Datastore configuration options
A set of common configuration options are available from the MongoDB Datastore builder, regardless of the actual implementation used.
These common configuration options allows to:
-
Configure the default read preference to use.
-
Configure the default read concern to use.
-
Configure the default write concern to use.
-
Add a new bson
Codec
. -
Add a new bson
CodecProvider
.
MongoDatastore datastore = MongoDatastore.builder().client(getMongoClient()).database("my_db")
.readPreference(ReadPreference.primary()) (1)
.readConcern(ReadConcern.MAJORITY) (2)
.writeConcern(WriteConcern.UNACKNOWLEDGED) (3)
.withCodec(getCustomCodec()) (4)
.withCodecProvider(getCustomCodecProvider()) (5)
.build();
1 | Set the default read preference |
2 | Set the default read concern |
3 | Set the default write concern |
4 | Add a new bson Codec |
5 | Add a new bson CodecProvider |
3.1.3. Enum
codec strategy
By default, enumeration type document field values are mapped to a Java Enum
class using a name based matching strategy.
An ordinal based matching strategy is also available, using the Enum
ordinal position to map an enumeration type document field value to a Java Enum
class.
The enumeration codec strategies are listed in the EnumCodecStrategy enumeration and the strategy to use can be configured using the MongoDB Datastore builder enumCodecStrategy
method.
MongoDatastore datastore = MongoDatastore.builder().client(getMongoClient()).database("my_db")
.enumCodecStrategy(EnumCodecStrategy.ORDINAL) (1)
.build();
1 | Set the enumeration codec strategy to ORDINAL |
3.1.4. Common Datastore API configuration options
Every MongoDB Datastore builder, regardless of the actual implementation, extends the core Datastore builder API, which provides the common Datastore configuration settings listed below.
Builder method | Arguments | Description |
---|---|---|
|
The data context id |
Set the data context id to which the Datastore is bound. Can be used, for example, to declare configuration properties for multiple Datastore instances. |
|
|
Whether to enable Datastore operations tracing. When enabled, the MongoDB Datastore will log the JSON representation of the documents involved in the performed datastore operations. |
|
A |
Set the |
Example:
MongoDatastore datastore = MongoDatastore.builder().client(getMongoClient()).database("my_db")
.dataContextId("mydataContextId") (1)
.traceEnabled(true) (2)
.build();
1 | Set a data context id for the Datastore |
2 | Activate operations tracing in log |
The configuration properties can also be provided through an external configuration property source, using the properties provided by the DatastoreConfigProperties property set.
For example, supposing to have a properties file named datastore.properties
like this:
holon.datastore.trace=true
We can use it as configuration property source to enable the Datastore tracing mode:
MongoDatastore datastore = MongoDatastore.builder().client(getMongoClient()).database("my_db")
.configuration(DatastoreConfigProperties.builder().withPropertySource("datastore.properties").build()) (1)
.build();
1 | Use the datastore.properties file as configuration property source |
3.2. Property model and MongoDB documents mapping
The Holon Platform PropertyBox type, used by the Datastore
API to collect and handle the data model attributes values using the Property model, is mapped into a MongoDB BSON Document using the following conventions:
-
Each
PropertyBox
instance is mapped into a MongoDB BSON document, using the property set bound to thePropertyBox
instance to determine the available document nodes names. -
Each property name is mapped to a document node name. By default only the
Path
type properties are used for mapping, using the path name as node name. -
Each property value available from the
PropertyBox
instance is mapped into a document node value, performing suitable data type convesions if required.
3.2.1. Nested documents
Nested MongoDB documents can be represented using either:
1. Nested property names using a dot notation:
When nested path names are included in the property set, either using the dot notation or parent path declarations, they will be mapped as nested document properties in the MongoDB BSON Document.
For example:
static final StringProperty NAME = StringProperty.create("name");
static final StringProperty STREET = StringProperty.create("address.street");
static final StringProperty CITY = StringProperty.create("address.city");
static final PropertySet<?> SUBJECT = PropertySet.of(NAME, STREET, CITY);
The SUBJECT
property set will be mapped into a Document with:
-
A
name
String type node at root level. -
A nested
address
document with thestreet
andcity
String type nodes.
2. Nested PropertyBox
type properties:
A PropertyBox
type property can be used to represent a nested document. The nested document nodes will be the one represented by the property set bound to PropertyBox
type property.
For this purpose, the PropertyBoxProperty type can be used, which makes available a builder to create a new PropertyBox
type property providing the property set to use.
For example:
static final StringProperty NAME = StringProperty.create("name");
static final StringProperty STREET = StringProperty.create("street");
static final StringProperty CITY = StringProperty.create("city");
static final PropertyBoxProperty ADDRESS = PropertyBoxProperty.create("address", STREET, CITY);
static final PropertySet<?> SUBJECT = PropertySet.of(NAME, ADDRESS);
The SUBJECT
property set will be mapped into a Document with the same schema of the previous example, i.e.:
-
A
name
String type node at root level. -
A nested
address
document with thestreet
andcity
String type nodes.
3.2.2. Multi value document nodes
Multi value (array) MongoDB Document nodes can be represented using a Collection
type property.
The standard SetPathProperty and ListPathProperty types can be used to represent a Set
or List
type collection of values.
static final StringProperty NAME = StringProperty.create("name");
static final ListPathProperty<String> SKILLS = ListPathProperty.create("skills", String.class); (1)
static final SetPathProperty<EnumValue> QUALIFICATIONS = SetPathProperty.create("qualifications",
EnumValue.class); (2)
static final PropertySet<?> SUBJECT = PropertySet.of(NAME, SKILLS, QUALIFICATIONS);
1 | Will be mapped into an array type Document node value and represented as a Java List |
2 | Will be mapped into an array type Document node value and represented as a Java Set |
3.3. Document id
The MongoDB Datastore supports three data types to represent a Document id and to map it to a property:
1: The standard org.bson.types.ObjectId
BSON type:
static final PathProperty<ObjectId> ID = PathProperty.create("_id", ObjectId.class);
2: The String
type:
static final StringProperty ID = StringProperty.create("_id");
1: The BigInteger
type:
static final NumericProperty<BigInteger> ID = NumericProperty.bigIntegerType("_id");
The MongoDB Datastore will perform all the suitable conversions, if required, during the document mapping into and from a PropertyBox
type.
Regarding the document id naming strategy, the following options can be used:
1: If a single property is declared as property set identifier and its type is one of the valid document id types (see before), it is used as document id. In this scenario, the property name is irrelevant and any path name can be used.
static final StringProperty ID = StringProperty.create("my_document_id");
static final StringProperty NAME = StringProperty.create("name");
static final PropertySet<?> SUBJECT = PropertySet.builderOf(ID, NAME).withIdentifier(ID).build(); (1)
1 | The my_document_id named property will be used as document id, because it is explicitly declared as property set identifier |
2: Otherwise, if a property with the default _id
name is available in the propert set and its type is one of the valid document id types (see before), that property is used as document id.
static final StringProperty ID = StringProperty.create("_id");
static final StringProperty NAME = StringProperty.create("name");
static final PropertySet<?> SUBJECT = PropertySet.of(ID, NAME); (1)
1 | The _id String type property will be used as document id, because it is named with the default _id name and its type is a valid document id type |
When a document id property name different form the standard id is used, the MongoDB Datastore _save, insert and update operations will always include a _id document attribute anyway and its value will be synchonized with the property value declared as document id.
|
3.4. Database collections
For each Datastore operation, the target database collection name must be specified using the DataTarget
representation, providing the collection name as data target name.
final static DataTarget<?> TARGET = DataTarget.named("my_collection"); (1)
1 | Declare a DataTarget for the my_collection database collection name |
3.5. Auto-generated ids
The MongoDB Datastore API supports the retrieving of the auto-generated document id values.
The auto-generated document id values can be obtained from the OperationResult
object, returned by the Datastore data manipulation operations, through the getInsertedKeys()
and related methods.
final PathProperty<ObjectId> ID = PathProperty.create("_id", ObjectId.class);
Datastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
PropertyBox value = buildPropertyBoxValue();
OperationResult result = datastore.insert(DataTarget.named("my_collection"), value); (1)
Optional<ObjectId> idValue = result.getInsertedKey(ID); (2)
idValue = result.getFirstInsertedKey(ObjectId.class); (3)
1 | Perform a insert operation using the Datastore API |
2 | Get the document id value using the ID property, if available |
3 | Get the first auto-generated document id value |
The default BRING_BACK_GENERATED_IDS WriteOption
can be provided to the Datastore
API operation to bring back any auto-generated document id value into the PropertyBox
instance which was the subject of the operation.
The auto-generated document id value will be setted in the PropertyBox
instance using the property which acts as property set identifier. See Document id.
final StringProperty ID = StringProperty.create("_id"); (1)
final PathProperty<String> TEXT = PathProperty.create("text", String.class);
Datastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
PropertyBox value = PropertyBox.builder(ID, TEXT).set(TEXT, "test").build(); (2)
datastore.insert(DataTarget.named("my_collection"), value, DefaultWriteOption.BRING_BACK_GENERATED_IDS); (3)
String idValue = value.getValue(ID); (4)
1 | Document id property declaration using the standard _id name |
2 | Create the PropertyBox value to insert |
3 | Perform the insert operation, providing the BRING_BACK_GENERATED_IDS write option |
4 | The ID property value of the inserted PropertyBox is updated with the auto-generated document id value |
3.6. Synchronous MongoDB Datastore
The holon-datastore-mongo-sync
artifact provides the synchronous MongoDB Datastore
API implementation.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-sync</artifactId>
<version>5.5.0</version>
The MongoDatastore interface represents the synchronous MongoDB Datastore API implementation, extending the Datastore API.
A synchronous MongoDB Java driver is required in classpath in order for the datastore to work. |
The MongoDatastore
API, besides the standard Datastore
API operations, provides a builder()
method which can be used to create and configure a MongoDatastore
API istance.
A com.mongodb.client.MongoClient
reference is required for the MongoDatastore
instance setup.
MongoDatastore datastore = MongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new MongoDatastore instance |
2 | Set the MongoDB client to use |
If you want to reach the goal of a complete abstraction from the persistence store technology and the persistence model, the core Datastore API interface should be used instead of the specific MongoDatastore API by your application code. This way, the concrete Datastore API implementation may be replaced by a different one at any time, without any change to the codebase.
|
To reach the goal of a complete abstraction from the persistence store technology and the persistence model, the core Datastore
API interface should be used by your application code, instead of the specific MongoDatastore
API:
Datastore datastore = MongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new MongoDatastore instance and expose it as a Datastore API type |
2 | Set the MongoDB client to use |
For example, given the following property model declaration:
final static StringProperty ID = StringProperty.create("_id"); (1)
final static StringProperty NAME = StringProperty.create("name"); (2)
final static PropertySet<?> SUBJECT = PropertySet.builderOf(ID, NAME).withIdentifier(ID).build(); (3)
final static DataTarget<?> TARGET = DataTarget.named("my_collection"); (4)
1 | Document id property using the standard _id name and String as value type |
2 | A String type document property declation which maps the name document attribute |
3 | Property set declaration including the ID and NAME properties. The ID property is explicitly declared as the identifier property, even if in this case it would not have been necessary because the ID property is mapped to the standard _id document id attribute name. |
4 | Data target declation using the my_collection database collection name |
Below an example of some datastore operations using the synchronous MongoDB Datastore API:
Datastore datastore = MongoDatastore.builder().client(getMongoClient()).database("test").build(); (1)
PropertyBox value = PropertyBox.builder(SUBJECT).set(NAME, "My name").build();
OperationResult result = datastore.save(TARGET, value); (2)
Optional<String> documentId = result.getInsertedKey(ID); (3)
result = datastore.insert(TARGET, value, DefaultWriteOption.BRING_BACK_GENERATED_IDS); (4)
documentId = value.getValueIfPresent(ID); (5)
value.setValue(NAME, "Updated name");
datastore.update(TARGET, value); (6)
datastore.delete(TARGET, value); (7)
long count = datastore.query(TARGET).filter(NAME.contains("fragment").or(NAME.startsWith("prefix"))).count(); (8)
Stream<PropertyBox> results = datastore.query(TARGET).filter(NAME.isNotNull()).sort(ID.desc()).stream(SUBJECT); (9)
Optional<String> id = datastore.query(TARGET).filter(NAME.eq("My name")).findOne(ID); (10)
result = datastore.bulkUpdate(TARGET).set(NAME, "Updated").filter(NAME.isNull()).execute(); (11)
long affected = result.getAffectedCount();
result = datastore.bulkDelete(TARGET).filter(NAME.endsWith("suffix")).execute(); (12)
affected = result.getAffectedCount();
1 | Build a MongoDB Datastore bound to the test database name |
2 | Save (insert if not present or update otherwise) given PropertyBox value in the my_collection database collection name (using the TARGET declaration) |
3 | Get the auto-generated document id value, mapped to the ID property declaration |
4 | Insert given value using the BRING_BACK_GENERATED_IDS default write option |
5 | The auto-generated document id value will be setted in the provided PropertyBox value, bound to the ID property |
6 | Update given value |
7 | Delete the value |
8 | Perform a count query on the my_collection database collection, providing some filters |
9 | Perform a query on the my_collection database collection, providing a filter and a sort declaration and projecting the query results as a stream of PropertyBox values |
10 | Perform a query to obtain a single value of the ID property, if available |
11 | Perform a bulk update |
12 | Perform a bulk delete |
3.7. Asynchronous MongoDB Datastore
The holon-datastore-mongo-async
artifact provides the asynchronous MongoDB AsyncDatastore
API implementation.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-async</artifactId>
<version>5.5.0</version>
The AsyncMongoDatastore interface represents the asynchronous MongoDB Datastore API implementation, extending the AsyncDatastore API.
An asynchronous MongoDB Java driver is required in classpath in order for the datastore to work. |
The AsyncMongoDatastore
API, besides the standard AsyncDatastore
API operations, provides a builder()
method which can be used to create and configure an AsyncMongoDatastore
API istance.
A com.mongodb.reactivestreams.client.MongoClient
reference is required for the AsyncMongoDatastore
instance setup.
AsyncMongoDatastore datastore = AsyncMongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new AsyncMongoDatastore instance |
2 | Set the MongoDB client to use |
If you want to reach the goal of a complete abstraction from the persistence store technology and the persistence model, the AsyncDatastore API interface should be used instead of the specific AsyncMongoDatastore API by your application code. This way, the concrete AsyncDatastore API implementation may be replaced by a different one at any time, without any change to the codebase.
|
To reach the goal of a complete abstraction from the persistence store technology and the persistence model, the AsyncDatastore
API interface should be used by your application code, instead of the specific AsyncMongoDatastore
API:
AsyncDatastore datastore = AsyncMongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new AsyncMongoDatastore instance and expose it as a AsyncDatastore API type |
2 | Set the MongoDB client to use |
The AsyncDatastore
API uses the java.util.concurrent.CompletionStage
interface to provide the asynchronous operation results.
Below an example of some datastore operations using the asynchronous MongoDB Datastore API:
AsyncDatastore datastore = AsyncMongoDatastore.builder().client(getMongoClient()).database("test").build(); (1)
PropertyBox value = PropertyBox.builder(SUBJECT).set(NAME, "My name").build();
datastore.save(TARGET, value) (2)
.thenApply(result -> result.getInsertedKey(ID)).thenAccept(id -> {
Optional<String> documentId = id; (3)
});
datastore.insert(TARGET, value, DefaultWriteOption.BRING_BACK_GENERATED_IDS) (4)
.thenApply(result -> value.getValueIfPresent(ID)).thenAccept(id -> {
Optional<String> documentId = id; (5)
});
value.setValue(NAME, "Updated name");
CompletionStage<OperationResult> result = datastore.update(TARGET, value); (6)
result = datastore.delete(TARGET, value); (7)
CompletionStage<Long> count = datastore.query(TARGET)
.filter(NAME.contains("fragment").or(NAME.startsWith("prefix"))).count(); (8)
CompletionStage<Stream<PropertyBox>> results = datastore.query(TARGET).filter(NAME.isNotNull()).sort(ID.desc())
.stream(SUBJECT); (9)
CompletionStage<Optional<String>> id = datastore.query(TARGET).filter(NAME.eq("My name")).findOne(ID); (10)
datastore.bulkUpdate(TARGET).set(NAME, "Updated").filter(NAME.isNull()).execute().thenAccept(r -> { (11)
long affected = r.getAffectedCount();
});
datastore.bulkDelete(TARGET).filter(NAME.endsWith("suffix")).execute().thenAccept(r -> { (12)
long affected = r.getAffectedCount();
});
1 | Build a MongoDB AsyncDatastore bound to the test database name |
2 | Save (insert if not present or update otherwise) given PropertyBox value in the my_collection database collection name (using the TARGET declaration) |
3 | Get the auto-generated document id value, mapped to the ID property declaration |
4 | Insert given value using the BRING_BACK_GENERATED_IDS default write option |
5 | The auto-generated document id value will be setted in the provided PropertyBox value, bound to the ID property |
6 | Update given value |
7 | Delete the value |
8 | Perform a count query on the my_collection database collection, providing some filters |
9 | Perform a query on the my_collection database collection, providing a filter and a sort declaration and projecting the query results as a stream of PropertyBox values |
10 | Perform a query to obtain a single value of the ID property, if available |
11 | Perform a bulk update |
12 | Perform a bulk delete |
3.8. Reactive MongoDB Datastore
The holon-datastore-mongo-reactor
artifact provides the reactive MongoDB AsyncDatastore
API implementation, using the Project Reactor Mono
and Flux
to provide and handle the datastore operations results.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-reactor</artifactId>
<version>5.5.0</version>
The ReactiveMongoDatastore interface represents the asynchronous MongoDB Datastore API implementation, extending the ReactiveDatastore API.
A reactive MongoDB Java driver is required in classpath in order for the datastore to work. |
The ReactiveMongoDatastore
API, besides the standard ReactiveDatastore
API operations, provides a builder()
method which can be used to create and configure a ReactiveMongoDatastore
API istance.
A com.mongodb.reactivestreams.client.MongoClient
reference is required for the ReactiveMongoDatastore
instance setup.
ReactiveMongoDatastore datastore = ReactiveMongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new ReactiveMongoDatastore instance |
2 | Set the MongoDB client to use |
If you want to reach the goal of a complete abstraction from the persistence store technology and the persistence model, the ReactiveDatastore API interface should be used instead of the specific ReactiveMongoDatastore API by your application code. This way, the concrete ReactiveDatastore API implementation may be replaced by a different one at any time, without any change to the codebase.
|
To reach the goal of a complete abstraction from the persistence store technology and the persistence model, the ReactiveDatastore
API interface should be used by your application code, instead of the specific ReactiveMongoDatastore
API:
ReactiveDatastore datastore = ReactiveMongoDatastore.builder() (1)
.client(getMongoClient()) (2)
.build();
1 | Obtain a builder to configure and create a new ReactiveMongoDatastore instance and expose it as a ReactiveDatastore API type |
2 | Set the MongoDB client to use |
The ReactiveDatastore
API uses the Project Reactor reactor.core.publisher.Mono
and reactor.core.publisher.Flux
objects to provide the operation results.
Below an example of some datastore operations using the reactive MongoDB Datastore API:
ReactiveDatastore datastore = ReactiveMongoDatastore.builder().client(getMongoClient()).database("test")
.build(); (1)
PropertyBox value = PropertyBox.builder(SUBJECT).set(NAME, "My name").build();
datastore.save(TARGET, value) (2)
.map(result -> result.getInsertedKey(ID)).doOnSuccess(id -> {
Optional<String> documentId = id; (3)
});
datastore.insert(TARGET, value, DefaultWriteOption.BRING_BACK_GENERATED_IDS) (4)
.doOnSuccess(id -> {
Optional<String> documentId = value.getValueIfPresent(ID); (5)
});
value.setValue(NAME, "Updated name");
datastore.update(TARGET, value); (6)
datastore.delete(TARGET, value); (7)
Mono<Long> count = datastore.query(TARGET).filter(NAME.contains("fragment").or(NAME.startsWith("prefix")))
.count(); (8)
Flux<PropertyBox> results = datastore.query(TARGET).filter(NAME.isNotNull()).sort(ID.desc()).stream(SUBJECT); (9)
Mono<String> id = datastore.query(TARGET).filter(NAME.eq("My name")).findOne(ID); (10)
Mono<OperationResult> result = datastore.bulkUpdate(TARGET).set(NAME, "Updated").filter(NAME.isNull())
.execute(); (11)
result.doOnSuccess(r -> {
long affected = r.getAffectedCount();
});
result = datastore.bulkDelete(TARGET).filter(NAME.endsWith("suffix")).execute(); (12)
result.doOnSuccess(r -> {
long affected = r.getAffectedCount();
});
1 | Build a MongoDB ReactiveDatastore bound to the test database name |
2 | Save (insert if not present or update otherwise) given PropertyBox value in the my_collection database collection name (using the TARGET declaration) |
3 | Get the auto-generated document id value, mapped to the ID property declaration |
4 | Insert given value using the BRING_BACK_GENERATED_IDS default write option |
5 | The auto-generated document id value will be setted in the provided PropertyBox value, bound to the ID property |
6 | Update given value |
7 | Delete the value |
8 | Perform a count query on the my_collection database collection, providing some filters |
9 | Perform a query on the my_collection database collection, providing a filter and a sort declaration and projecting the query results as a Flux of PropertyBox values |
10 | Perform a query to obtain a single value of the ID property, if available |
11 | Perform a bulk update |
12 | Perform a bulk delete |
3.9. Custom BSON filters and sorts
The MongoDB Datastore APIs support custom BSON filters and sorts expressions, i.e. QueryFilter
and QuerySort
type expressions for which the BSON expression that represents the filtering and sorting conditions is directly provided.
3.9.1. BsonFilter
The BsonFilter interface is a QueryFilter
implementation to provide the query restrictions using a BSON expression, and it can be used just like any other QueryFilter
implementation in query definitions.
Datastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
long count = datastore.query(DataTarget.named("my_collection"))
.filter(BsonFilter.create(Filters.size("my_field", 3))) (1)
.count();
1 | Use BsonFilter to provide a BSON filter expression that matches all documents where the value of the my_field field is an array of the specified size (3) |
3.9.2. BsonSort
The BsonSort interface is a QuerySort
implementation to provide the query sort using a BSON expression, and it can be used just like any other QuerySort
implementation in query definitions.
Datastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
Stream<String> values = datastore.query(DataTarget.named("my_collection"))
.sort(BsonSort.create(Sorts.descending("my_field"))) (1)
.stream(MY_PROPERTY);
1 | Use BsonSort to provide a BSON sort specification for a descending sort on the my_field field |
3.10. Transactions management
The MongoDB Datastore API implementations support transactions management, if transaction support is available from the concrete MongoDB database engine.
The multi-document ACID transactions support is available since the version 4.0 of MongoDB platform and the MongoDB Java driver version 3.8 or higher is required. |
The transactions management operations are made available using:
-
The Transactional API for the synchronous MongoDB Datastore implementation.
-
The AsyncTransactional API for the asynchronous MongoDB Datastore implementation.
-
The ReactiveTransactional API for the reactive MongoDB Datastore implementation.
3.10.1. Synchronous transactions management
The isTransactional()
or requireTransactional()
methods of the Datastore
API can be used to obtain the transactional API which can be used for transactions management.
The Transaction interface is used as transaction representation and provides methods to inspect the transaction status and perform the commit and rollback operations.
final Datastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
datastore.requireTransactional().withTransaction(tx -> { (1)
datastore.save(TARGET, value);
tx.commit(); (2)
});
OperationResult result = datastore.requireTransactional().withTransaction(tx -> { (3)
return datastore.save(TARGET, value);
}, TransactionConfiguration.withAutoCommit()); (4)
1 | Obtain the Transactional API to execute one or more Datastore operation within a transaction |
2 | Commit the transaction |
3 | Obtain the Transactional API to execute the Datastore operation within a transaction and return a value |
4 | The transaction is configured with the auto commit mode, this way the transaction is automatically committed at the transactional operation end if no error occurred |
3.10.2. Asynchronous transactions management
The isTransactional()
or requireTransactional()
methods of the AsyncDatastore
API can be used to obtain the transactional API which can be used for transactions management.
The AsyncTransaction interface is used as transaction representation and provides methods to inspect the transaction status and perform the commit and rollback operations.
final AsyncDatastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
CompletionStage<Boolean> committed = datastore.requireTransactional().withTransaction(tx -> { (1)
datastore.save(TARGET, value);
return tx.commit(); (2)
});
CompletionStage<OperationResult> result = datastore.requireTransactional().withTransaction(tx -> { (3)
return datastore.save(TARGET, value);
}, TransactionConfiguration.withAutoCommit()); (4)
1 | Obtain the AsyncTransactional API to execute one or more Datastore operation within a transaction |
2 | Commit the transaction |
3 | Obtain the AsyncTransactional API to execute the Datastore operation within a transaction and return a value |
4 | The transaction is configured with the auto commit mode, this way the transaction is automatically committed at the transactional operation end if no error occurred |
3.10.3. Reactive transactions management
The isTransactional()
or requireTransactional()
methods of the ReactiveDatastore
API can be used to obtain the transactional API which can be used for transactions management.
The ReactiveTransaction interface is used as transaction representation and provides methods to inspect the transaction status and perform the commit and rollback operations.
final ReactiveDatastore datastore = getMongoDatastore(); // build or obtain a MongoDB Datastore
Flux<Boolean> committed = datastore.requireTransactional().withTransaction(tx -> { (1)
datastore.save(TARGET, value);
return tx.commit().flux(); (2)
});
Flux<OperationResult> result = datastore.requireTransactional().withTransaction(tx -> { (3)
return datastore.save(TARGET, value).flux();
}, TransactionConfiguration.withAutoCommit()); (4)
1 | Obtain the ReactiveTransactional API to execute one or more Datastore operation within a transaction |
2 | Commit the transaction |
3 | Obtain the ReactiveTransactional API to execute the Datastore operation within a transaction and return a value |
4 | The transaction is configured with the auto commit mode, this way the transaction is automatically committed at the transactional operation end if no error occurred |
3.11. Using the MongoDatabaseHandler
for a direct database access
The MongoDatabaseHandler API can be used obtain a direct reference to the MongoDatabase
object which represents the Mongo database configured for the current Datastore implementation.
The MongoDatabase
reference can be used to perform database operations with the MongoDB Java driver directly, using BSON expressions and outside of the standard Datastore API operations.
Each specific MongoDB datastore implementation provides the MongoDatabaseHandler
support, using a consistent MongoDatabase
reference type.
MongoDatastore datastore = getMongoDatastore();
long count = datastore.withDatabase(db -> { (1)
db.createCollection("test");
return db.getCollection("test").countDocuments();
});
1 | Get the MongoDatabase reference and use it to perform some operations |
3.13. Expression resolvers
The Datastore
API can be extended using the ExpressionResolver
API, to add new expression resolution strategies, modify existing ones and to handle new Expression
types.
See the Datastore API extension documentation section for details. |
3.13.1. MongoDB Expression resolvers registration
A new ExpressionResolver
can be registered in the MongoDB Datastore
API in two ways:
1. Using the MongoDB Datastore
API instance:
An ExpressionResolver
can be registered either using the Datastore
builder API at Datastore configuration time:
Datastore datastore = MongoDatastore.builder() //
.withExpressionResolver(new MyExpressionResolver()) (1)
.build();
1 | Register a new ExpressionResolver |
Or using the Datastore
API itself, which extends the ExpressionResolverSupport
API:
datastore.addExpressionResolver(new MyExpressionResolver()); (1)
1 | Register and new ExpressionResolver |
The same registration options are available for the AsyncDatastore and ReactiveDatastore API and builders.
|
2. Automatic registration using Java service extensions:
The MongoDB Datastore implementatons support ExpressionResolver
automatic registration using the MongoDatastoreExpressionResolver base type and the default Java service extensions modality.
To automatically register an ExpressionResolver
this way, a class implementing MongoDatastoreExpressionResolver
has to be created and its fully qualified name must be specified in a file named com.holonplatform.datastore.mongo.core.config.MongoDatastoreExpressionResolver
, placed in the META-INF/services
folder in classpath.
When this registration method is used, the expression resolvers defined this way will be registered for any MongoDB Datastore API instance.
3.13.2. Specific expression resolvers registration
All the default Datastore
API operations supports operation specific expression resolvers registration, through the ExpressionResolverSupport
API.
An ExpressionResolver
registered for a specific Datastore
API operation execution will be available only for the execution of that operation, and will be ignored by any other Datastore
API operation.
For example, to register an expression resolver only for a single Query
execution, the Query
builder API can be used:
long result = datastore.query().target(DataTarget.named("test")) //
.withExpressionResolver(new MyExpressionResolver()) (1)
.count();
1 | Register an expression resolver only for the specific Query operation definition |
3.13.3. Expression resolvers priority
According to the standard convention, the javax.annotation.Priority
annotation can be used on ExpressionResolver
classes to indicate in what order the expression resolvers bound to the same type resolution pair (i.e. the expression type handled by a resolver and the target expression type into which it will be resolved) must be applied.
The less is the javax.annotation.Priority
number assigned to a resolver, the higher will be it’s priority order.
All the default MongoDB Datastore expression resolvers have the minimum priority order, allowing to override their behavior and resolution strategies with custom expression resolvers with a higher assigned priority order (i.e. a priority number less then Integer.MAX_VALUE
).
3.13.4. Expression validation
The internal MongoDB Datastore BSON composer engine will perform validation on any Expression
instance to resolve and each corresponding resolved Expression
instance, using the default expression validate()
method.
So the validate()
method can be used to implement custom expression validation logic and throw an InvalidExpressionException
when validation fails.
3.13.5. MongoDB Datastore expressions
Besides the standard Datastore API expressions, such as DataTarget
, QueryFilter
and QuerySort
, which can be used to extend the Datastore
API with new expression implementations and new resolution strategies, the MongoDB Datastore
API can be extended using a set of specific BSON resolution expressions, used by the internal BSON composer engine to implement the resolution and composition strategy to obtain the BSON expressions and document mappings from the Datastore
API meta-language expressions.
These expressions are available from the com.holonplatform.datastore.mongo.core.expression
package of the holon-datastore-mongo-core
artifact.
The BsonExpression is the expression which represents a BSON expression part, used to compose the actual BSON statement which will be executed using the MongoDB Java driver.
The BsonExpression
type can be used to directly resolve an abstract Datastore
API expression into a BSON statement part.
For example, supposing to have a IdIs
class which represents a QueryFilter
expression type to represent the expression "the _id
column name value is equal to a given ObjectId type value":
class IdIs implements QueryFilter {
private final ObjectId value;
public IdIs(ObjectId value) {
this.value = value;
}
public ObjectId getValue() {
return value;
}
@Override
public void validate() throws InvalidExpressionException {
if (value == null) {
throw new InvalidExpressionException("Id value must be not null");
}
}
}
We want to create an ExpressionResolver
class to resolve the IdIs
expression directly into a BSON epression, using the BsonExpression
type. Using the convenience create
method of the ExpressionResolver
API, we can do it in the following way:
final ExpressionResolver<IdIs, BsonExpression> keyIsResolver = ExpressionResolver.create( //
IdIs.class, (1)
BsonExpression.class, (2)
(keyIs, ctx) -> Optional.of(BsonExpression.create(Filters.eq("_id", keyIs.getValue())))); (3)
1 | Expression type to resolve |
2 | Target expression type |
3 | Expression resolution logic: since we resolve the IdIs expression directly into a BsonExpression type, the BSON filter representation is provided |
After the ExpressionResolver
is registered in the Datastore
API, the new IdIs
expression can be used in the Datastore
API operations which support the QueryFilter
expression type just like any other filter expression. For example, in a Query
expression:
Datastore datastore = MongoDatastore.builder().withExpressionResolver(keyIsResolver) (1)
.build();
Query query = datastore.query().filter(new IdIs(new ObjectId("xxxx"))); (2)
1 | Register the new expression resolver |
2 | Use the IdIs expression in a query definition |
Other expression types are used to represent the elements of a query or a Datastore operation. These expression types often represent an intermediate expression type, between the highest abstract layer (i.e. an expression of the Datastore
API meta-language) and the final BSON expression representation.
An example is the BsonProjection to represent a query projection.
3.13.6. MongoDB Expression resolution context
The MongoDB Datastore APIs make available an extension of the standard expression ResolutionContext
, to provide a set of configuration attributes and BSON resolution context specific operations.
This resolution context extension is represented by the MongoResolutionContext API.
3.14. Commodity factories
The MongoDB Datastore
API supports Datastore commodities registration using a suitable commodity factory type, which is specific for each MongoDB datastore implementation:
-
For the synchronous
Datastore
implementation, the SyncMongoDatastoreCommodityFactory type. -
For the asynchronous
AsyncDatastore
and the reactiveReactiveDatastore
implementation, the AsyncMongoDatastoreCommodityFactory type.
See the Datastore commodities definition and registration documentation section to learn how the Datastore commodity architecture can be used to provide extensions to the default Datastore API.
|
These commodity factory types provide a specialized MongoDatastoreCommodityContext API extension as commodity context, to make available a set of MongoDB specific configuration attributes and references, for example:
-
The current database name and reference.
-
The current client session, if available.
-
The current codec registry.
-
The default read preference, read concern and write concern.
-
The available expression resolvers.
Furthermore, it makes available some API methods to interact with the underlying database, such as the withDatabase(MongoDatabaseOperation operation)
method.
Example: definition of a commodity which provides a method to create a database collection:
interface MyCommodity extends DatastoreCommodity { (1)
void createCollection(String name);
}
class MyCommodityImpl implements MyCommodity { (2)
private final MongoDatabaseHandler<MongoDatabase> databaseHandler;
public MyCommodityImpl(MongoDatabaseHandler<MongoDatabase> databaseHandler) {
super();
this.databaseHandler = databaseHandler;
}
@Override
public void createCollection(String name) {
databaseHandler.withDatabase(db -> {
db.createCollection(name);
});
}
}
class MyCommodityFactory implements SyncMongoDatastoreCommodityFactory<MyCommodity> { (3)
@Override
public Class<? extends MyCommodity> getCommodityType() {
return MyCommodity.class;
}
@Override
public MyCommodity createCommodity(SyncMongoDatastoreCommodityContext context)
throws CommodityConfigurationException {
return new MyCommodityImpl(context);
}
}
1 | Datastore commodity API |
2 | Commodity implementation |
3 | Commodity factory implementation |
A Datastore commodity factory class which extends the SyncMongoDatastoreCommodityFactory
or the AsyncMongoDatastoreCommodityFactory
interface can be registered in a MongoDB Datastore in two ways:
1. Direct registration using the MongoDB Datastore API builder:
The MongoDB Datastore APIs supports the commodity factory registration using the withCommodity
builder method.
Datastore datastore = MongoDatastore.builder() //
.withCommodity(new MyCommodityFactory()) (1)
.build();
1 | Register the MyCommodityFactory commodity factory in given Datastore implementation |
2. Automatic registration using the Java service extensions:
To automatically register an commodity factory using the standard Java service extensions based method, a class implementing either SyncMongoDatastoreCommodityFactory
or AsyncMongoDatastoreCommodityFactory
has to be created and its qualified full name must be specified in a file named com.holonplatform.datastore.mongo.sync.config.SyncMongoDatastoreCommodityFactory
or com.holonplatform.datastore.mongo.async.config.AsyncMongoDatastoreCommodityFactory
, placed in the META-INF/services
folder of the classpath.
When this registration method is used, the commodity factories defined this way will be registered for any MongoDB Datastore API instance.
4. Spring ecosystem integration
The holon-datastore-mongo-spring
artifact provides integration with the Spring framework for the MongoDB Datastore APIs.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-spring</artifactId>
<version>5.5.0</version>
4.1. Enable a MongoDB Datastore bean
A set of Spring configuration class annotations are provided to enable a MongoDB datastore implementation and make it available as a Spring bean.
4.1.1. Synchronous MongoDB Datastore
For the synchronous Datastore
implementation, the EnableMongoDatastore annotation can be used on a Spring configuration class.
To enable a MongoDB Datastore
bean the following preconditions must be met:
-
A synchronous MongoDB
com.mongodb.client.MongoClient
type bean must be available. By default, themongoClient
bean name is used to detect the MongoClient bean reference. The client reference bean name can be changed using themongoClientReference()
annotation attribute. -
A database name must be specified using the
database()
annotation attribute.
@Configuration
@EnableMongoDatastore(database = "test") (1)
class ConfigSync {
@Bean
public com.mongodb.client.MongoClient mongoClient() { (2)
return com.mongodb.client.MongoClients.create();
}
}
@Autowired
Datastore datastore; (3)
1 | Use test as database name |
2 | MongoClient bean definition |
3 | A synchronous Datastore bean is configured and available from Spring context |
4.1.2. Asynchronous MongoDB Datastore
For the asynchronous AsyncDatastore
implementation, the EnableMongoAsyncDatastore annotation can be used on a Spring configuration class.
To enable a MongoDB AsyncDatastore
bean the following preconditions must be met:
-
An asynchronous MongoDB
com.mongodb.reactivestreams.client.MongoClient
type bean must be available. By default, themongoClient
bean name is used to detect the MongoClient bean reference. The client reference bean name can be changed using themongoClientReference()
annotation attribute. -
A database name must be specified using the
database()
annotation attribute.
@Configuration
@EnableMongoAsyncDatastore(database = "test") (1)
class ConfigAsync {
@Bean
public com.mongodb.reactivestreams.client.MongoClient mongoClient() { (2)
return com.mongodb.reactivestreams.client.MongoClients.create();
}
}
@Autowired
AsyncDatastore asyncDatastore; (3)
1 | Use test as database name |
2 | MongoClient bean definition |
3 | An asynchronous AsyncDatastore bean is configured and available from Spring context |
4.1.3. Reactive MongoDB Datastore
For the reactive ReactiveDatastore
implementation, the EnableMongoReactiveDatastore annotation can be used on a Spring configuration class.
To enable a MongoDB ReactiveDatastore
bean the following preconditions must be met:
-
An asynchronous MongoDB
com.mongodb.reactivestreams.client.MongoClient
type bean must be available. By default, themongoClient
bean name is used to detect the MongoClient bean reference. The client reference bean name can be changed using themongoClientReference()
annotation attribute. -
A database name must be specified using the
database()
annotation attribute.
@Configuration
@EnableMongoReactiveDatastore(database = "test") (1)
class ConfigReactive {
@Bean
public com.mongodb.reactivestreams.client.MongoClient mongoClient() { (2)
return com.mongodb.reactivestreams.client.MongoClients.create();
}
}
@Autowired
ReactiveDatastore reactiveDatastore; (3)
1 | Use test as database name |
2 | MongoClient bean definition |
3 | A reactive ReactiveDatastore bean is configured and available from Spring context |
4.2. MongoDB Datastore bean configuration
All the annotations described above to enable a MongoDB Datastore bean provide a set of configuration options, which can be setted using the annotations attributes.
4.2.1. MongoClient bean name
By default, the mongoClient
bean name is used to detect the MongoClient
bean reference to use and bind to the MongoDB Datastore implementation. The client reference bean name can be changed using the mongoClientReference()
annotation attribute.
@Configuration
@EnableMongoDatastore(database = "test", mongoClientReference = "syncMongoClient") (1)
@EnableMongoAsyncDatastore(database = "test", mongoClientReference = "asyncMongoClient") (2)
class Config {
@Bean
public com.mongodb.client.MongoClient syncMongoClient() {
return com.mongodb.client.MongoClients.create();
}
@Bean
public com.mongodb.reactivestreams.client.MongoClient asyncMongoClient() {
return com.mongodb.reactivestreams.client.MongoClients.create();
}
}
@Autowired
Datastore datastore;
@Autowired
AsyncDatastore asyncDatastore;
1 | The syncMongoClient bean name is used to enable the synchronous Datastore bean |
2 | The asyncMongoClient bean name is used to enable the asynchronous AsyncDatastore bean |
4.2.2. Enum codec strategy
The enumeration type value codec strategy can be configured using the enumCodecStrategy()
annotation attribute. See Enum
codec strategy for details.
@Configuration
@EnableMongoDatastore(database = "test", enumCodecStrategy = EnumCodecStrategy.ORDINAL) (1)
class Config {
@Bean
public com.mongodb.client.MongoClient mongoClient() {
return com.mongodb.client.MongoClients.create();
}
}
1 | Set ORDINAL as enumeration codec strategy |
4.2.3. Mongo database configuration
The following annotation attributes can be used to set the default read preference, read concern and write concern to use:
-
The
readPreference()
annotation attribute can be use to set the default read preference. -
The
readConcern()
annotation attribute can be use to set the default read concern. -
The
writeConcern()
annotation attribute can be use to set the default write concern.
@Configuration
@EnableMongoDatastore(database = "test", readPreference = MongoReadPreference.PRIMARY, (1)
readConcern = MongoReadConcern.LOCAL, (2)
writeConcern = MongoWriteConcern.ACKNOWLEDGED (3)
)
class Config2 {
@Bean
public com.mongodb.client.MongoClient mongoClient() {
return com.mongodb.client.MongoClients.create();
}
}
1 | Set the default read preference |
2 | Set the default read concern |
3 | Set the default write concern |
4.2.4. Primary mode
Each annotation provides a primary()
attribute which can be used to control the primary mode of the MongoDB Datastore bean registration.
If the primary mode is set to PrimaryMode.TRUE
, the Datastore
bean created with the corresponding annotation will be marked as primary in the Spring application context, meaning that will be the one provided by Spring in case of multiple available candidates, when no specific bean name or qualifier is specified in the dependency injection declaration.
This behaviour is similar to the one obtained with the Spring @Primary annotation at bean definition time.
|
By default, the primary mode is set to PrimaryMode.AUTO
, meaning that the registred MongoDB Datastore bean will be marked as primary only when the MongoClient
bean to which is bound is registered as primary candidate bean.
4.2.5. MongoDB Datastore configuration properties
When a Mongo Datastore bean is configured using one of the available annotations (@EnableMongoDatastore
, @EnableMongoAsyncDatastore
, @EnableMongoReactiveDatastore
), the Spring environment is automatically used as configuration properties source.
This way, many Datastore configuration settings can be provided using a configuration property with the proper name and value.
The supported configuration properties are:
1. The standard Datastore configuration properties, avaible from the DatastoreConfigProperties property set (See Datastore configuration).
The configuration property prefix is holon.datastore
and the following properties are available:
Name | Type | Meaning |
---|---|---|
holon.datastore. trace |
Boolean ( |
Enable/disable Datastore operations tracing. |
2. An additional set of properties, provided by the MongoDatastoreConfigProperties property set, which can be used as an alternative for the annotations attributes described in the previous sections.
Name | Type | Meaning |
---|---|---|
holon.datastore.mongo. database |
|
Set the database name to which the Datastore is bound. |
holon.datastore.mongo. primary |
Boolean ( |
Mark the MongoDB Datastore bean as primary candidate for dependency injection when more than one definition is available. |
holon.datastore.mongo. read-preference |
A valid |
Set the default read preference to use. |
holon.datastore.mongo. read-concern |
A valid |
Set the default read concern to use. |
holon.datastore.mongo. write-concern |
A valid |
Set the default write concern to use. |
holon.datastore.mongo. enum-codec-strategy |
A valid |
The enumeration type values codec strategy to use. See |
Example of Datastore configuration properties:
holon.datastore.trace=true (1)
holon.datastore.mongo.database=test (2)
holon.datastore.mongo.read-preference=PRIMARY (3)
1 | Enable tracing |
2 | Set the database name to test |
3 | Set the default read preference to PRIMARY |
4.2.6. Datastore extension and configuration using the Spring context
The MongoDB Datastore implementation supports the standard Holon Platform Datastore Spring integration features for Datastore beans configuration and extension, which includes:
-
Datastore configuration post processing using DatastorePostProcessor type beans.
-
Datastore extension through ExpressionResolver registration using DatastoreResolver annotated beans.
-
Datastore commodity factory registration using DatastoreCommodityFactory annotated beans.
@DatastoreResolver (1)
class MyFilterExpressionResolver implements QueryFilterResolver<MyFilter> {
@Override
public Class<? extends MyFilter> getExpressionType() {
return MyFilter.class;
}
@Override
public Optional<QueryFilter> resolve(MyFilter expression, ResolutionContext context)
throws InvalidExpressionException {
// implement actual MyFilter expression resolution
return Optional.empty();
}
}
@Component
class MyDatastorePostProcessor implements DatastorePostProcessor { (2)
@Override
public void postProcessDatastore(ConfigurableDatastore datastore, String datastoreBeanName) {
// configure Datastore
}
}
@DatastoreCommodityFactory (3)
class MyCommodityFactory implements SyncMongoDatastoreCommodityFactory<MyCommodity> {
@Override
public Class<? extends MyCommodity> getCommodityType() {
return MyCommodity.class;
}
@Override
public MyCommodity createCommodity(SyncMongoDatastoreCommodityContext context)
throws CommodityConfigurationException {
// create commodity instance
return new MyCommodity();
}
}
1 | Automatically register a Datastore expression resolver using the @DatastoreResolver annotation |
2 | Post process Datastore configuration using a DatastorePostProcessor type Spring bean |
3 | Automatically register a Datastore commodity factory using the @DatastoreCommodityFactory annotation |
When a Mongo Datastore bean is configured using one of the available annotations (@EnableMongoDatastore
, @EnableMongoAsyncDatastore
, @EnableMongoReactiveDatastore
), the Datastore extension and configuration using the Spring context is automatically enabled.
When one of the available annotation is not used to configure the MongoDB datastore bean, the Datastore extension and configuration support can be explicitly enabled using the @EnableDatastoreConfiguration
annotation on Spring configuration classes.
4.3. Multiple MongoDB Datastores configuration
When more than one MongoDB Datastore bean has to be configured using the @Enable*
annotations, the dataContextId
attribute can be used to assign a different data context id to each MongoDB Datastore bean definition, in order to:
-
Provide different sets of configuration properties using the same Spring environment.
-
Provide a default name pattern matching strategy with the
MongoClient
bean definition to use for each MongoDB Datastore to configure: if not directly specified with themongoClientReference()
attribute, theMongoClient
bean definition to use for each MongoDB Datastore will be detected in Spring context using the bean name pattern:mongoClient_{datacontextid}
where{datacontextid}
is equal to thedataContextId
attribute of the annotation.
When a data context id is defined, a Spring qualifier named the same as the data context id will be associated to the generated MongoDB Datastore bean definitions, and such qualifier can be later used to obtain the right MongoDB Datastore instance through dependency injection.
@Configuration class Config {
@Configuration
@EnableMongoDatastore(database = "test", dataContextId = "one") (1)
static class Config1 {
@Bean(name = "mongoClient_one")
public com.mongodb.client.MongoClient mongoClient() {
return com.mongodb.client.MongoClients.create();
}
}
@Configuration
@EnableMongoDatastore(database = "test", dataContextId = "two") (2)
static class Config2 {
@Bean(name = "mongoClient_two")
public com.mongodb.client.MongoClient mongoClient() {
return com.mongodb.client.MongoClients.create();
}
}
}
@Autowired
@Qualifier("one") (3)
Datastore datastore1;
@Autowired
@Qualifier("two")
Datastore datastore2;
1 | Configure the first MongoDB Datastore using one as data context id: by default the bean named mongoClient_one will be used as MongoClient |
2 | Configure the second MongoDB Datastore using two as data context id: by default the bean named mongoClient_two will be used as MongoClient |
3 | A specific Datastore type bean reference can be obtained using the data context id as qualifier |
5. Spring Boot integration
The holon-datastore-mongo-spring-boot
artifact provides integration with Spring Boot for MongoDB Datastores auto-configuration.
To enable the Spring Boot MongoDB Datastore auto-configuration features, the following artifact must be included in your project dependencies:
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-datastore-mongo-spring-boot</artifactId>
<version>5.5.0</version>
The MongoDB Datastore auto-configuration class support the configuration of the three available MongoDB Datastore implementations:
5.1. Synchronous MongoDB Datastore
A synchronous MongoDB Datastore
is auto-configured only when:
-
A
MongoDatastore
type bean is not already available from the Spring application context. -
A valid
com.mongodb.client.MongoClient
type bean is available from the Spring application context.
5.2. Asynchronous MongoDB Datastore
An asynchronous MongoDB AsyncDatastore
is auto-configured only when:
-
A
AsyncMongoDatastore
type bean is not already available from the Spring application context. -
A valid
com.mongodb.reactivestreams.client.MongoClient
type bean is available from the Spring application context.
5.3. Reactive MongoDB Datastore
A reactive MongoDB ReactiveDatastore
is auto-configured only when:
-
A
ReactiveMongoDatastore
type bean is not already available from the Spring application context. -
A valid
com.mongodb.reactivestreams.client.MongoClient
type bean is available from the Spring application context. -
The
holon-datastore-mongo-reactor
artifact (and the Project Reactor core library) is available from classpath.
5.4. MongoDB Datastore configuration
The Spring Boot application properties can be used to configure the MongoDB Datastore beans, for example:
holon:
datastore:
trace: true
mongo:
database: test
5.5. Disabling the MongoDB Datastore auto-configuration feature
To disable this auto-configuration feature the MongoDatastoreAutoConfiguration class can be excluded:
@EnableAutoConfiguration(exclude={MongoDatastoreAutoConfiguration.class})
5.6. Spring Boot starters
The following starter artifacts are available to provide a quick project configuration setup using Maven dependency system:
1. Default MongoDB Datastore starter provides the dependencies to the Holon MongoDB Datastore Spring Boot integration artifact holon-datastore-mongo-spring-boot
and the synchronous and asynchronous MongoDB Datastore implementations.
The MongoDB Java driver library is included in the starters dependencies.
Furthermore, the following additional dependencies are provided:
-
The Holon Platform Core Module Spring Boot integration base starter (
holon-starter
). -
The base Spring Boot starter (
spring-boot-starter
), see the Spring Boot starters documentation for details.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-starter-mongo-datastore</artifactId>
<version>5.5.0</version>
2. Reactive MongoDB Datastore starter provides the same dependencies as the default MongoDB Datastore starter, adding the holon-datastore-mongo-reactor
artifact to provide the reactive MongoDB Datastore implementation and auto-configuration.
Maven coordinates:
<groupId>com.holon-platform.mongo</groupId>
<artifactId>holon-starter-mongo-datastore-reactor</artifactId>
<version>5.5.0</version>
6. Loggers
By default, the Holon platform uses the SLF4J API for logging. The use of SLF4J is optional: it is enabled when the presence of SLF4J is detected in the classpath. Otherwise, logging will fall back to JUL (java.util.logging
).
The logger name for the MongoDB Datastore module is com.holonplatform.datastore.mongo
.
7. System requirements
7.1. Java
The Holon Platform JDBC Datastore module requires Java 8 or higher.
7.2. MongoDB Java Drivers
The MongoDB Java Driver version 3.5+ is required to use this module.
The MongoDB Java Driver version 3.8+ is recommended for a full compatibility with the module APIs, for example to use the transactions management support.
For asynchronous and reactive interaction support, the MongoDB Reactive Streams Java Driver is required.