ODM Framework
The uma::bson API added a simple function pointer based Object-Document Mapping (ODM) framework in version 2.5. This makes it possible for client applications/libraries to create domain models that use the various uma::bson::Value instances to store data and inherit from the abstract uma::bson::ODMObject class. This extends the previous simple ODM made available through uma::bson::Object (ODMObject inherits from Object)
The primary requirements for using the ODM framework are the following:
- Inherit from the uma::bson::ODMObject class and specify the class as the template parameter. The actual type is used for the MetaField implementation that is used to store the function pointers to the accessor and mutator methods for the fields (similar to the model used for POD or POJO type objects).
- Declare all serialisable fields as instances of uma::bson::Value.
- Register the serialisable fields by creating a MetaFieldImpl instance with the value type as template parameter, field name (may be aliased differently for bson I/O), function pointer to the accessor method for the field (needs to be non-const), and function pointer to the mutator method for the field and invoking the
registerField()
method.
The registration process should ideally be done in each constructor implemented (or designated constructor if using C++11), and performed only if
registered()
returns false
.Header
The class contains two string fields which store the name and model identifier. Standard accessor and mutator methods are defined for these fields. The common method used to register the fields are implemented in a private registerFields method which is invoked from the constructors.
#ifndef SAMPLE_ODM_MANUFACTURERODM_H #define SAMPLE_ODM_MANUFACTURERODM_H #include <uma/bson/ODMObject.h> #include <uma/bson/String.h> namespace sample { namespace odm { class ManufacturerODM : public uma::bson::ODMObject<ManufacturerODM> { public: ManufacturerODM(); ManufacturerODM( const std::string& name, const std::string& model ); uma::bson::String& getName() { return name; } const uma::bson::String& getName() const { return name; } void setName( const uma::bson::String& n ) { name = n; } uma::bson::String& getModel() { return model; } const uma::bson::String& getModel() const { return model; } void setModel( const uma::bson::String& m ) { model = m; } private: void registerFields(); private: uma::bson::String name; uma::bson::String model; }; } // namespace odm } // namespace sample #endif // SAMPLE_ODM_MANUFACTURERODM_H
Implementation
The implementation file provides the implementation for the constructors and the private
registerFields
method. Note that the registration is wrapped within a check to see if the current class has already been registered. This is the recommended pattern to follow, since there is no reason to re-register the class for the various instances that may be created during application runtime. We had some issues with static registration using a macro, hence this round-about way for performing the registration (we have generally had no luck with getting static registration to work with MSVC).#include "ManufacturerODM.h" using namespace sample::odm; ManufacturerODM::ManufacturerODM() { registerFields(); } ManufacturerODM::ManufacturerODM( const std::string& nm, const std::string& mo ) : name( nm ), model( mo ) { registerFields(); } void ManufacturerODM::registerFields() { if ( ! registered( this ) ) { registerField( new MetaFieldImpl<uma::bson::String>( "name", &ManufacturerODM::getName, &ManufacturerODM::setName ) ); registerField( new MetaFieldImpl<uma::bson::String>( "model", &ManufacturerODM::getModel, &ManufacturerODM::setModel ) ); } }
Client Code
Client code is nearly identical to that using the simple callback based ODM framework.
sample::odm::ManufacturerODM manufacturer; manufacturer.setName( “BlackBerry” ); manufacturer.setModel( “Q10” ); // a little later in your code std::stringstream ss; manufacturer.toBson( ss ); // serialise as BSON data stream. // Another part of your code sample::odm::ManufacturerODM mobj; mobj.populate( ss ); // de-serialise data from BSON stream if ( manufacturer != mobj ) throw “Serialisation failed”;
Notice that there is no need to implement the
getFieldNames()
or getValue( const std::string& )
methods from the uma::bson::Object class. Registering the serialisable fields provides the information necessary for the default implementation of these methods.