Mapping support Rich maping support is provided by the MongoMappingConverter. MongoMappingConverter has a rich metadata model that provides a full feature set of functionality to map domain objects into MongoDB documents. The mapping metadata model is populated using annotations on your domain objects but is not limited to using annotations as the only source of metadata information. In this section we will describe the features of the MongoMappingConverter and how to specify annotation based mapping metadata.
MongoDB Mapping Configuration You can configure the MongoMappingConverter as well as Mongo and MongoTemplate eithe using Java or XML based metadata. Here is an example using Spring's Java based configuration @Configuration class to configure MongoDB mapping support @Configuration public class GeoSpatialAppConfig extends AbstractMongoConfiguration { @Bean public Mongo mongo() throws Exception { return new Mongo("localhost"); } @Bean public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(mongo(), "geospatial", "newyork", mappingMongoConverter()); } // specify which package to scan for @Document objects. public String getMappingBasePackage() { return "org.springframework.data.document.mongodb"; } // optional @Bean public LoggingEventListener<MongoMappingEvent> mappingEventsListener() { return new LoggingEventListener<MongoMappingEvent>(); } } AbstractMongoConfiguration requires you to implement methods that define a Mongo as well as a MongoTemplate object to the container. AbstractMongoConfiguration also has a method you can override named 'getMappingBasePackage' which tells the configuration where to scan for classes annotated with the @org.springframework.data.document.mongodb.mapping.Document annotation. Spring's Mongo namespace enables you to easily enable mapping functionality in XML XML schema to configure MongoDB mapping support <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Default bean name is 'mongo' --> <mongo:mongo host="localhost" port="27017"/> <!-- by default look for a Mongo object named 'mongo' - default name used for the converter is 'mappingConverter' --> <mongo:mapping-converter base-package="com.mycompany.domain"/> <!-- set the mapping converter to be used by the MongoTemplate --> <bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate"> <constructor-arg name="mongo" ref="mongo" /> <constructor-arg name="databaseName" value="test" /> <constructor-arg name="defaultCollectionName" value="myCollection" /> <constructor-arg name="mongoConverter" ref="mappingConverter"/> </bean> </beans This sets up the right objects in the ApplicationContext to perform the full gamut of mapping operations. The base-package property tells it where to scan for classes annotated with the @org.springframework.data.document.mongodb.mapping.Document annotation.
Mapping Framework Usage To take full advantage of the object mapping functionality inside the Spring Data/MongoDB support, you should annotate your mapped objects with the @org.springframework.data.document.mongodb.mapping.Document annotation. Although it is not necessary for the mapping framework to have this annotation (your POJOs will be mapped correctly, even without any annotations), it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. If you don't use this annotation, your application will take a slight performance hit the first time you store a domain object because the mapping framework needs to build up its internal metadata model so it knows about the properties of your domain object and how to persist them. Example domain object package com.mycompany.domain; @Document public class Person { @Id private ObjectId id; @Indexed private Integer ssn; private String firstName; @Indexed private String lastName; } The @Id annotation tells the mapper which property you want to use for the MongoDB _id property and the @Indexed annotation tells the mapping framework to call ensureIndex on that property of your document, making searches faster.
Mapping annotation overview The MappingMongoConverter relies on metadata to drive the mapping of objects to documents. An overview of the annotations is provided below @Id - applied at the field level to mark the field used for identiy purpose. @Document - applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the name of the collection where the database will be stored. @DBRef - applied at the field to indicate it is to be stored using a com.mongodb.DBRef. @Indexed - applied at the field level to describe how to index the field. @CompoundIndex - applied at the type level to declare Compound Indexes @GeoSpatialIndexed - applied at the field level to describe how to geoindex the field. @Transient - by default all private fields are mapped to the document, this annotation excludes the field where it is applied from being stored in the database @PersistenceConstructor - marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved DBObject. @Value - this annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object. The mapping metadata infrastructure is defined in a seperate spring-data-commons project that is technology agnostic. Specific subclasses are using in the Mongo support to support annotation based metadata. Other strategies are also possible to put in place if there is demand. Here is an example of a more complex mapping. @Document @CompoundIndexes({ @CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}") }) public class Person<T extends Address> { @Id private String id; @Indexed(unique = true) private Integer ssn; private String firstName; @Indexed private String lastName; private Integer age; @Transient private Integer accountTotal; @DBRef private List<Account> accounts; private T address; public Person(Integer ssn) { this.ssn = ssn; } @PersistenceConstructor public Person(Integer ssn, String firstName, String lastName, Integer age, T address) { this.ssn = ssn; this.firstName = firstName; this.lastName = lastName; this.age = age; this.address = address; } public String getId() { return id; } // no setter for Id. (getter is only exposed for some unit testing) public Integer getSsn() { return ssn; } // other getters/setters ommitted }
Id fields The @Id annotation is applied to fields. MongoDB lets you store any type as the _id field in the database, including long and string. It is of course common to use ObjectId for this purpose. If the value on the @Id field is not null, it is stored into the database as-is. If it is null, then the converter will assume you want to store an ObjectId in the database. For this to work the field type should be either ObjectId, String, or BigInteger.
Compound Indexes Compound indexes are also supported. They are defined at the class level, rather than on indidvidual properties. Here's an example that creates a compound index of lastName in ascending order and age in descending order: Example Compound Index Usage package com.mycompany.domain; @Document @CompoundIndexes({ @CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}") }) public class Person { @Id private ObjectId id; private Integer age; private String firstName; private String lastName; }
Using DBRefs The mapping framework doesn't have to store child objects embedded within the document. You can also store them separately and use a DBRef to refer to that document. When the object is loaded from MongoDB, those references will be eagerly resolved and you will get back a mapped object that looks the same as if it had been stored embedded within your master document. Here's an example of using a DBRef to refer to a specific document that exists independently of the object in which it is referenced (both classes are shown in-line for brevity's sake): @Document public class Account { @Id private ObjectId id; private Float total; } @Document public class Person { @Id private ObjectId id; @Indexed private Integer ssn; @DBRef private List<Account> accounts; } There's no need to use something like @OneToMany because the mapping framework sees that you're wanting a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the Account objects themselves. The mapping framework does not handle cascading saves. If you change an Account object that is referenced by a Person object, you must save the Account object separately. Calling save on the Person object will not automatically save the Account objects in the property accounts.
Mapping Framework Events Events are fired throughout the lifecycle of the mapping process. This is described in the Lifecycle Events section. Simply declaring these beans in your Spring ApplicationContext will cause them to be invoked whenever the event is dispatched.
Overriding Mapping with explicit Converters When storing and querying your objects it is convenient to have a MongoConverter instance handle the mapping of all Java types to DBObjects. However, sometimes you may want the MongoConverter's do most of the work but allow you to selectivly handle the conversion for a particular type. To do this, register one or more one or more org.springframework.core.convert.converter.Converter instances with the MongoConverter. Spring 3.0 introduced a core.convert package that provides a general type conversion system. This is described in detail in the Spring reference documentation section entitled Spring 3 Type Conversion. The setConverters method on SimpleMongoConverter and MappingMongoConverter should be used for this purpose. The method afterMappingMongoConverterCreation in AbstractMongoConfiguration can be overriden to configure a MappingMongoConverter.