Overriding Default Mapping with Custom Converters

To have more fine-grained control over the mapping process, you can register Spring converters with the MongoConverter implementations, such as the MappingMongoConverter.

The MappingMongoConverter checks to see if any Spring converters can handle a specific class before attempting to map the object itself. To 'hijack' the normal mapping strategies of the MappingMongoConverter, perhaps for increased performance or other custom mapping needs, you first need to create an implementation of the Spring Converter interface and then register it with the MappingConverter.

For more information on the Spring type conversion service, see the reference docs here.

Saving by Using a Registered Spring Converter

The following example shows an implementation of the Converter that converts from a Person object to a org.bson.Document:

import org.springframework.core.convert.converter.Converter;

import org.bson.Document;

public class PersonWriteConverter implements Converter<Person, Document> {

  public Document convert(Person source) {
    Document document = new Document();
    document.put("_id", source.getId());
    document.put("name", source.getFirstName());
    document.put("age", source.getAge());
    return document;
  }
}

Reading by Using a Spring Converter

The following example shows an implementation of a Converter that converts from a Document to a Person object:

public class PersonReadConverter implements Converter<Document, Person> {

  public Person convert(Document source) {
    Person p = new Person((ObjectId) source.get("_id"), (String) source.get("name"));
    p.setAge((Integer) source.get("age"));
    return p;
  }
}

Registering Spring Converters with the MongoConverter

The Mongo Spring namespace provides a convenient way to register Spring Converter instances with the MappingMongoConverter. The following configuration snippet shows how to manually register converter beans as well as configure the wrapping MappingMongoConverter into a MongoTemplate:

<mongo:db-factory dbname="database"/>

<mongo:mapping-converter>
  <mongo:custom-converters>
    <mongo:converter ref="readConverter"/>
    <mongo:converter>
      <bean class="org.springframework.data.mongodb.test.PersonWriteConverter"/>
    </mongo:converter>
  </mongo:custom-converters>
</mongo:mapping-converter>

<bean id="readConverter" class="org.springframework.data.mongodb.test.PersonReadConverter"/>

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
  <constructor-arg name="mongoConverter" ref="mappingConverter"/>
</bean>

You can also use the base-package attribute of the custom-converters element to enable classpath scanning for all Converter and GenericConverter implementations below the given package, as the following example shows:

<mongo:mapping-converter>
  <mongo:custom-converters base-package="com.acme.**.converters" />
</mongo:mapping-converter>

Converter Disambiguation

Generally, we inspect the Converter implementations for the source and target types they convert from and to. Depending on whether one of those is a type MongoDB can handle natively, we register the converter instance as a reading or a writing converter. The following examples show a writer converter and a read converter (note the difference is in the order of the qualifiers on Converter):

// Write converter as only the target type is one Mongo can handle natively
class MyConverter implements Converter<Person, String> { … }

// Read converter as only the source type is one Mongo can handle natively
class MyConverter implements Converter<String, Person> { … }

If you write a Converter whose source and target type are native Mongo types, we cannot determine whether we should consider it as a reading or a writing converter. Registering the converter instance as both might lead to unwanted results. For example, a Converter<String, Long> is ambiguous, although it probably does not make sense to try to convert all String instances into Long instances when writing. To let you force the infrastructure to register a converter for only one way, we provide @ReadingConverter and @WritingConverter annotations to be used in the converter implementation.