Files
spring-data-mongodb/src/docbkx/reference/mapping.xml

411 lines
15 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="mongo.mapping">
<title>Mapping support</title>
<para>Rich maping support is provided by the
<classname>MongoMappingConverter</classname>.
<classname>MongoMappingConverter</classname> 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.</para>
<section id="mongodb:mapping-configuration">
<title>MongoDB Mapping Configuration</title>
<para>You can configure the MongoMappingConverter as well as Mongo and
MongoTemplate eithe using Java or XML based metadata.</para>
<para>Here is an example using Spring's Java based configuration</para>
<example>
<title>@Configuration class to configure MongoDB mapping support</title>
<programlisting language="xml">
@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&lt;MongoMappingEvent&gt; mappingEventsListener() {
return new LoggingEventListener&lt;MongoMappingEvent&gt;();
}
}</programlisting>
</example>
<para><classname>AbstractMongoConfiguration</classname> requires you to
implement methods that define a <classname>Mongo</classname> as well as a
<classname>MongoTemplate</classname> object to the container.
<classname>AbstractMongoConfiguration</classname> also has a method you
can override named '<methodname>getMappingBasePackage</methodname>' which
tells the configuration where to scan for classes annotated with the
<classname>@org.springframework.data.document.mongodb.mapping.Document</classname>
annotation.</para>
<para>Spring's Mongo namespace enables you to easily enable mapping
functionality in XML</para>
<example>
<title>XML schema to configure MongoDB mapping support</title>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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"&gt;
&lt;!-- Default bean name is 'mongo' --&gt;
&lt;mongo:mongo host="localhost" port="27017"/&gt;
&lt;!-- by default look for a Mongo object named 'mongo' - default name used for the converter is 'mappingConverter' --&gt;
&lt;mongo:mapping-converter base-package="com.mycompany.domain"/&gt;
&lt;!-- set the mapping converter to be used by the MongoTemplate --&gt;
&lt;bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate"&gt;
&lt;constructor-arg name="mongo" ref="mongo" /&gt;
&lt;constructor-arg name="databaseName" value="test" /&gt;
&lt;constructor-arg name="defaultCollectionName" value="myCollection" /&gt;
&lt;constructor-arg name="mongoConverter" ref="mappingConverter"/&gt;
&lt;/bean&gt;
&lt;/beans
</programlisting>
</example>
<para>This sets up the right objects in the ApplicationContext to perform
the full gamut of mapping operations. The <code>base-package</code>
property tells it where to scan for classes annotated with the
<classname>@org.springframework.data.document.mongodb.mapping.Document</classname>
annotation.</para>
</section>
<section id="mongodb:mapping-usage">
<title>Mapping Framework Usage</title>
<para>To take full advantage of the object mapping functionality inside
the Spring Data/MongoDB support, you should annotate your mapped objects
with the
<classname>@org.springframework.data.document.mongodb.mapping.Document</classname>
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.</para>
<example>
<title>Example domain object</title>
<programlisting language="java">package com.mycompany.domain;
@Document
public class Person {
@Id
private ObjectId id;
@Indexed
private Integer ssn;
private String firstName;
@Indexed
private String lastName;
}
</programlisting>
</example>
<important>
<para>The <classname>@Id</classname> annotation tells the mapper which
property you want to use for the MongoDB <code>_id</code> property and
the <classname>@Indexed</classname> annotation tells the mapping
framework to call <code>ensureIndex</code> on that property of your
document, making searches faster.</para>
</important>
<section>
<title>Mapping annotation overview</title>
<para>The MappingMongoConverter relies on metadata to drive the mapping
of objects to documents. An overview of the annotations is provided
below</para>
<itemizedlist>
<listitem>
<para><literal>@Id </literal>- applied at the field level to mark
the field used for identiy purpose.</para>
</listitem>
<listitem>
<para><literal>@Document</literal> - 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.</para>
</listitem>
<listitem>
<para><literal>@DBRef</literal> - applied at the field to indicate
it is to be stored using a com.mongodb.DBRef.</para>
</listitem>
<listitem>
<para><literal>@Indexed</literal> - applied at the field level to
describe how to index the field.</para>
</listitem>
<listitem>
<para><literal>@CompoundIndex</literal> - applied at the type level
to declare Compound Indexes</para>
</listitem>
<listitem>
<para><literal>@GeoSpatialIndexed</literal> - applied at the field
level to describe how to geoindex the field.</para>
</listitem>
<listitem>
<para><literal>@Transient</literal> - 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</para>
</listitem>
<listitem>
<para><literal>@PersistenceConstructor</literal> - 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.</para>
</listitem>
<listitem>
<para><literal>@Value</literal> - 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.</para>
</listitem>
</itemizedlist>
<para>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.</para>
<para>Here is an example of a more complex mapping.</para>
<programlisting>@Document
@CompoundIndexes({
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
})
public class Person&lt;T extends Address&gt; {
@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&lt;Account&gt; 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
}</programlisting>
<para></para>
</section>
<section>
<title>Id fields</title>
<para>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.</para>
</section>
<section id="mongodb:mapping-usage:indexes">
<title>Compound Indexes</title>
<para>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 <code>lastName</code> in ascending order and
<code>age</code> in descending order: <example>
<title>Example Compound Index Usage</title>
<programlisting language="java">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;
}
</programlisting>
</example></para>
</section>
<section id="mongodb:mapping-usage:references">
<title>Using DBRefs</title>
<para>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.</para>
<para>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):</para>
<example>
<programlisting language="java">
@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&lt;Account&gt; accounts;
}
</programlisting>
</example>
<para>There's no need to use something like <code>@OneToMany</code>
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
<code>Account</code> objects themselves. <important>
<para>The mapping framework does not handle cascading saves. If you
change an <code>Account</code> object that is referenced by a
<code>Person</code> object, you must save the Account object
separately. Calling <code>save</code> on the <code>Person</code>
object will not automatically save the <code>Account</code> objects
in the property <code>accounts</code>.</para>
</important></para>
</section>
<section id="mongodb:mapping-usage:events">
<title>Mapping Framework Events</title>
<para>Events are fired throughout the lifecycle of the mapping process.
This is described in the <link
linkend="mongodb.mapping-usage.events">Lifecycle Events</link>
section.</para>
<para>Simply declaring these beans in your Spring ApplicationContext
will cause them to be invoked whenever the event is dispatched.</para>
</section>
<section>
<title>Overriding Mapping with explicit Converters</title>
<para>When storing and querying your objects it is convenient to have a
<interfacename>MongoConverter</interfacename> instance handle the
mapping of all Java types to DBObjects. However, sometimes you may want
the <interfacename>MongoConverter</interfacename>'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
<classname>org.springframework.core.convert.converter.Converter</classname>
instances with the MongoConverter.</para>
<note>
<para>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 <ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert">Spring
3 Type Conversion</ulink>.</para>
</note>
<para>The <methodname>setConverters</methodname> method on
<classname>SimpleMongoConverter</classname> and
<classname>MappingMongoConverter</classname> should be used for this
purpose. The method
<methodname>afterMappingMongoConverterCreation</methodname> in
<classname>AbstractMongoConfiguration</classname> can be overriden to
configure a MappingMongoConverter.</para>
<para></para>
</section>
</section>
</chapter>