Elastic Social Manual / Version 2304
Table Of Contents
The Elastic Core persistence is based on instances of Model
s to
which the data that is stored in MongoDB is mapped at runtime.
The idea is that not the Java classes determine how the MongoDB documents are structured but the MongoDB document
is mapped to a given Java instance. Parts of the documents that do not fit the given Java instance are mapped into
a generic data pool to make sure that no data is lost when the Java instance is persisted back into the MongoDB
document just because the given Java instance does not understand them:
This mapping behavior offers a lot more flexibility to update Java classes without running into the hassles of schema evolution. For example, it allows for different Model classes accessing the same data at the same time. But it is different from typical mappers like Morphia, Spring Data for MongoDB or Hibernate that take a Java class as the source how to structure the data in the storage underneath.
Mapping properties
The mapping algorithm uses Java
Bean properties as entities to load and store data. That means if some Model
class
is used to load data via for example the ModelService
get(...) methods, the
Query
or the SearchService
, the mapping algorithm first creates an
instance of the given Model
class and then calls the setters of the instance to transfer
data from the MongoDB document to the instance. If a Java Bean
property is defined in the Model
instance,
its setter method is called by the mapping algorithm
and its value is accessible via the getter method. If no Java Bean
property is defined the data is stored in the generic data pool of the instance, which is accessible via
Model#getProperty()
.
If an instance of a Model
class is stored with Model#save()
or
ModelService#save()
, the mapping algorithm calls the getters of the given instance and
joins them with the generic data pool to map these properties into a MongoDB document. The key for storing of data
is the same combination of ID and Collection that was used to lookup the data.
In all implementations of this interface all setter methods for non-primitive types must support null values, even if a default value is used during initialization. Code or data migration might still cause the setter to be called with a null value.
Mapping atomic values
The following table describes the mapping of BSON values to the corresponding Java types:
BSON | Java |
---|---|
Boolean false/true | Boolean |
Floating point | double |
32-bit Integer | int |
64-bit Integer | long |
Boolean false/true |
java.lang.Boolean
|
UTC date time |
java.util.Date
|
Floating point |
java.lang.Double
|
32-bit Integer |
java.lang.Integer
|
64-bit Integer |
java.lang.Long
|
UTF-8 string |
java.lang.String
|
Object ID |
org.bson.types.ObjectId
|
Table 4.1. Mapping of BSON values to Java types
Mapping collection values
The following table describes the mapping of BSON collection values to the corresponding Java types:
BSON | Java |
---|---|
Array |
java.util.List
|
Embedded document |
java.util.Map
|
Table 4.2. Mapping of BSON collection values to Java types
Please note that the mapping is defined from BSON values to Java types which means that you are limited to
java.util.List
and java.util.Map
and cannot use the full expressiveness of the Java collection framework.
Mapping references
References to other Model
s or user defined classes are supported via
TypeConverter
s.
To make the implementation of custom TypeConverter
s easier, the helper class
AbstractTypeConverter
is there to provide a basic implementation for user defined types.
For Models
there is a specialized AbstractModelConverter
that provides a basic
implementation for user defined Models
.
The following table describes which Maven module contains support for the given types:
Module | Mapped Class |
---|---|
core-impl
|
com.coremedia.elastic.core.api.blobs.Blob
|
com.coremedia.elastic.core.api.models.Model
| |
com.coremedia.elastic.core.api.users.User
| |
java.lang.Enum
| |
java.lang.Locale
| |
social-impl
|
com.coremedia.elastic.social.api.comments.Comment
|
com.coremedia.elastic.social.api.reviews.Review
| |
com.coremedia.elastic.social.api.users.CommunityUser
| |
core-cms
|
com.coremedia.cap.content.Content
|
com.coremedia.objectserver.beans.ContentBean
| |
com.coremedia.xml.Markup
|
Table 4.3. Which module contains support for which type
MongoDB Collections and IDs
MongoDB documents are stored in
collections which can be seen as named
groupings of documents which share roughly the same structure or purpose. Indexes and queries are defined per
MongoDB collection. The key for the lookup of data in the MongoDB is the combination of ID and Collection. It is
accessible via Model#getId()
and Model#getCollection()
.
Extending models, users and comments
The basic idea to extend Model
s is to keep it simple for the API user, but hide and reuse
the implementation. You should never extend internal subclasses. Extending public interfaces is possible and
supported but not necessary. If you want to extend the API interfaces, create an interface and an implementation
for that aspect you are missing like this:
public interface FooUser extends User { String getFoo(); void setFoo(String foo); } public abstract class FooUserImpl implements FooUser { private String foo; public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } }
Example 4.1. Extending the API interfaces
Instances of the class above are enhanced with the internal implementation of Model
and
User
when calling UserService#createUser()
. Beware that this call
does not persist the returned instance to give the caller a possibility to modify the returned instance before
saving it with Model#save()
.
FooUser fooUser = userService.createUser("foos-id", FooUserImpl.class); fooUser.setFoo("foo"); fooUser.save();
Example 4.2. Modifying returned instance
When you already have a User
, just use
UserService#createFrom()
to turn it into FooUser
with a copy of the data that the User
had. All data from User is still readable and writable through the methods for the generic data pool:
User user = userService.getUserById("4711"); FooUser fooUser = userService.createFrom(user, FooUserImpl.class); fooUser.setFoo("bar"); fooUser.setProperty("name", "Foobar"); fooUser.save();
Example 4.3. Create user from existing user
Note
user
and fooUser
are different instances. Any changes to user are not visible
at the fooUser
instance. Saving a
modified user
and then a modified fooUser
in the scenario above will overwrite the changes applied to user.
Changing the class of an instance
ModelService#createFrom
may be used to change the class for a given
Model
instance without reloading the data from the underlying MongoDB document.