411 lines
15 KiB
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<MongoMappingEvent> mappingEventsListener() {
|
|
return new LoggingEventListener<MongoMappingEvent>();
|
|
}
|
|
|
|
|
|
}</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"><?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
|
|
</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<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
|
|
|
|
}</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<Account> 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>
|