Files
spring-data-mongodb/src/docbkx/reference/mongodb.xml
Mark Pollack e1f8eee2d1 DATADOC-88 - Create MongoDbFactory to consolidate DB, Server location, and user credentials into one location
DATADOC-147 - Update reference documentation to cover changes from M2 to M3 (partial work)
2011-05-24 18:07:18 -04:00

2104 lines
84 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.core">
<title>MongoDB support</title>
<para>The MongoDB support contains a wide range of features which are
summarized below.</para>
<itemizedlist>
<listitem>
<para>Spring configuration support using Java based @Configuration
classes or an XML namespace for a Mongo driver instance and replica
sets</para>
</listitem>
<listitem>
<para>MongoTemplate helper class that increases productivity performing
common Mongo operations. Includes integrated object mapping between
documents and POJOs.</para>
</listitem>
<listitem>
<para>Exception translation into Spring's portable Data Access Exception
hierarchy</para>
</listitem>
<listitem>
<para>Feature Rich Object Mapping integrated with Spring's Conversion
Service</para>
</listitem>
<listitem>
<para>Annotation based mapping metadata but extensible to support other
metadata formats</para>
</listitem>
<listitem>
<para>Persistence and mapping lifecycle events</para>
</listitem>
<listitem>
<para>Java based Query, Criteria, and Update DSLs</para>
</listitem>
<listitem>
<para>Automatic implementatin of Repository interfaces including support
for custom finder methods.</para>
</listitem>
<listitem>
<para>QueryDSL integration to support type-safe queries.</para>
</listitem>
<listitem>
<para>Cross-store persistance - support for JPA Entities with fields
transparently persisted/retrieved using MongoDB</para>
</listitem>
<listitem>
<para>Log4j log appender</para>
</listitem>
<listitem>
<para>GeoSpatial integration</para>
</listitem>
</itemizedlist>
<para>For most tasks you will find yourself using
<classname>MongoTemplate</classname> or the Repository support that both
leverage the rich mapping functionality. MongoTemplate is the place to look
for accessing functionality such as incrementing counters or ad-hoc CRUD
operations. MongoTemplate also provides callback methods so that it is easy
for you to get a hold of the low level API artifacts such as
<literal>org.mongo.DB</literal> to communicate directly with MongoDB. The
goal with naming conventions on various API artifacts is to copy those in
the base MongoDB Java driver so you can easily map your existing knowledge
onto the Spring APIs.</para>
<section id="mongodb-requirements">
<title>Getting Started</title>
<para>Spring MongoDB support requires MongoDB 1.4 or higher and Java SE 5
or higher. The latest production release (1.8.x as of this writing) is
recommended. An easy way to bootstrap setting up a working environment is
to create a Spring based project in <ulink
url="http://www.springsource.com/developer/sts">STS</ulink>.</para>
<para>First you need to set up a running Mongodb server. Refer to the
<ulink url="http://www.mongodb.org/display/DOCS/Quickstart">Mongodb Quick
Start guide</ulink> for an explanation on how to startup a Mongo instance.
Once installed starting Mongo is typically a matter of executing the
following command: <literal>MONGO_HOME/bin/mongod</literal></para>
<para>To create a Spring project in STS go to File -&gt; New -&gt; Spring
Template Project -&gt; Simple Spring Utility Project --&gt; press Yes when
prompted. Then enter a project and a package name such as
org.spring.mongodb.example.</para>
<para>Then add the following to pom.xml dependencies section.</para>
<programlisting lang="" language="xml">&lt;dependencies&gt;
&lt;!-- other dependency elements omitted --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.data&lt;/groupId&gt;
&lt;artifactId&gt;spring-data-mongodb&lt;/artifactId&gt;
&lt;version&gt;1.0.0.M3&lt;/version&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
</programlisting>
<para>Also change the version of Spring in the pom.xml to be</para>
<programlisting lang="" language="xml">&lt;spring.framework.version&gt;3.0.5.RELEASE&lt;/spring.framework.version&gt;</programlisting>
<para>You will also need to add the location of the Spring Milestone
repository for maven to your pom.xml which is at the same level of your
&lt;dependencies/&gt; element</para>
<programlisting language="xml">&lt;repositories&gt;
&lt;repository&gt;
&lt;id&gt;spring-milestone&lt;/id&gt;
&lt;name&gt;Spring Maven MILESTONE Repository&lt;/name&gt;
&lt;url&gt;http://maven.springframework.org/milestone&lt;/url&gt;
&lt;/repository&gt;
&lt;/repositories&gt;</programlisting>
<para>The repository is also <ulink
url="http://shrub.appspot.com/maven.springframework.org/milestone/org/springframework/data/">browseable
here</ulink>.</para>
<para>You may also want to set the logging level to DEBUG to see some
additional information, edit the log4j.properties file to have</para>
<programlisting>log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n</programlisting>
<para>Create a simple Person class to persist</para>
<programlisting language="java">package org.spring.mongodb.example;
public class Person {
private String id;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}</programlisting>
<para>And a main application to run</para>
<programlisting language="java">package org.spring.mongodb.example;
import static org.springframework.data.document.mongodb.query.Criteria.where;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query;
import com.mongodb.Mongo;
public class MongoApp {
private static final Log log = LogFactory.getLog(MongoApp.class);
public static void main(String[] args) throws Exception {
MongoOperations mongoOps = new MongoTemplate(new Mongo(), "database");
mongoOps.insert(new Person("Joe", 34));
log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));
mongoOps.dropCollection("person");
}
}</programlisting>
<para>This will produce the following output</para>
<programlisting>10:01:32,062 DEBUG apping.MongoPersistentEntityIndexCreator: 80 - Analyzing class class org.spring.example.Person for index information.
10:01:32,265 DEBUG work.data.document.mongodb.MongoTemplate: 631 - insert DBObject containing fields: [_class, age, name] in collection: Person
10:01:32,765 DEBUG work.data.document.mongodb.MongoTemplate:1243 - findOne using query: { "name" : "Joe"} in db.collection: database.Person
10:01:32,953 INFO org.spring.mongodb.example.MongoApp: 25 - Person [id=4ddbba3c0be56b7e1b210166, name=Joe, age=34]
10:01:32,984 DEBUG work.data.document.mongodb.MongoTemplate: 375 - Dropped collection [database.person]</programlisting>
<para>Even in this simple example, there are few things to take notice
of</para>
<itemizedlist>
<listitem>
<para>You can instantiate the central helper class of Spring Mongo,
<link linkend="mongo-template">MongoTemplate</link>, using the
standard <classname>com.mongodb.Mongo</classname> object and the name
of the database to use.</para>
</listitem>
<listitem>
<para>The mapper works against standard POJO objects without the need
for any additional metadata (though you can optionally provide that
information. See <link linkend="mongo.mapping">here</link>.).</para>
</listitem>
<listitem>
<para>Conventions are used for handling the id field, converting it to
be a ObjectId when stored in the database.</para>
</listitem>
<listitem>
<para>Mapping conventions can use field access. Notice the Person
class has only getters.</para>
</listitem>
<listitem>
<para>If the constructor argument names match the field names of the
stored document, they will be used to instantiate the object</para>
</listitem>
</itemizedlist>
<section id="mongodb-required-jars">
<title>Required Jars</title>
The following jars are required to use Sping Mongo
<itemizedlist>
<listitem>
<para>spring-data-mongodb-1.0.0.M3.jar</para>
</listitem>
</itemizedlist>
In addition to the above listed Spring Data jars you need to provide the following dependencies:
<itemizedlist>
<listitem>
<para>com.springsource.org.aopalliance-1.0.0.jar</para>
</listitem>
<listitem>
<para>commons-logging-1.1.1.jar</para>
</listitem>
<listitem>
<para>mongo-java-driver-2.5.3.jar</para>
</listitem>
<listitem>
<para>spring-aop-3.0.5.RELEASE.jar</para>
</listitem>
<listitem>
<para>spring-asm-3.0.5.RELEASE.jar</para>
</listitem>
<listitem>
<para>spring-beans-3.0.5.RELEASE.jar</para>
</listitem>
<listitem>
<para>spring-context-3I.0.5.RELEASE.jar</para>
</listitem>
<listitem>
<para>spring-core-3.0.5.RELEASE.jar</para>
</listitem>
<listitem>
<para>spring-expression-3.0.5.RELEASE.jar</para>
</listitem>
</itemizedlist>
</section>
</section>
<section>
<title>Examples Repository</title>
<para>There is an <ulink
url="https://github.com/SpringSource/spring-data-document-examples">github
repository with several examples</ulink> that you can download and play
around with to get a feel for how the library works.</para>
</section>
<section id="mongodb-connectors">
<title>Connecting to MongoDB with Spring</title>
<para>One of the first tasks when using MongoDB and Spring is to create a
<classname>com.mongodb.Mongo</classname> object using the IoC container.
There are two main ways to do this, either using Java based bean metadata
or XML based bean metadata. These are discussed in the following sections.
<note>
<para>For those not familiar with how to configure the Spring
container using Java based bean metadata instead of XML based metadata
see the high level introduction in the reference docs <ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html#new-java-configuration"
userlevel="">here </ulink> as well as the detailed documentation<ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-instantiating-container">
here</ulink>.</para>
</note></para>
<section>
<title>Registering a Mongo instance using Java based metadata</title>
<para>An example of using Java based bean metadata to register an
instance of a <classname>com.mongodb.Mongo</classname> is shown below
<example>
<title>Registering a com.mongodb.Mongo object using Java based bean
metadata</title>
<programlisting language="java">@Configuration
public class AppConfig {
/*
* Use the standard Mongo driver API to create a com.mongodb.Mongo instance.
*/
public @Bean Mongo mongo() throws UnknownHostException {
return new Mongo("localhost");
}
} </programlisting>
</example></para>
<para>This approach allows you to use the standard
<classname>com.mongodb.Mongo</classname> API that you may already be
used to using but also pollutes the code with the UnknownHostException
checked exception. The use of the checked exception is not desirable as
Java based bean metadata uses methods as a means to set object
dependencies, making the calling code cluttered.</para>
<para>An alternative is to register an instance of
<classname>com.mongodb.Mongo</classname> instance with the container
using Spring's<interfacename> MongoFactoryBean</interfacename>. As
compared to instantiating a <classname>com.mongodb.Mongo</classname>
instance directly, the FactoryBean approach does not throw a checked
exception and has the added advantage of also providing the container
with an ExceptionTranslator implementation that translates Mongo
exceptions to exceptions in Spring's portable
<classname>DataAccessException</classname> hierarchy for data access
classes annoated with the <literal>@Repository</literal> annotation.
This hierarchy and use of <literal>@Repository</literal> is described in
<ulink
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/dao.html">Spring's
DAO support features</ulink>.</para>
<para>An example of a Java based bean metadata that supports exception
translation on <classname>@Repository</classname> annotated classes is
shown below:</para>
<example>
<title>Registering a com.mongodb.Mongo object using Spring's
MongoFactoryBean and enabling Spring's exception translation
support</title>
<programlisting language="java">@Configuration
public class AppConfig {
/*
* Factory bean that creates the com.mongodb.Mongo instance
*/
public @Bean MongoFactoryBean mongo() {
MongoFactoryBean mongo = new MongoFactoryBean();
mongo.setHost("localhost");
return mongo;
}
}
</programlisting>
<para>To access the <classname>com.mongodb.Mongo</classname> object
created by the <classname>MongoFactoryBean</classname> in other
<literal>@Configuration</literal> or your own classes, use a
"<literal>private @Autowired Mongo mongo;</literal>" field.</para>
</example>
</section>
<section>
<title>Registering a Mongo instance using XML based metadata</title>
<para>While you can use Spring's traditional
<literal>&lt;beans/&gt;</literal> XML namespace to register an instance
of <classname>com.mongodb.Mongo</classname> with the container, the XML
can be quite verbose as it is general purpose. XML namespaces are a
better alternative to configuring commonly used objects such as the
Mongo instance. The mongo namespace alows you to create a Mongo instance
server location, replica-sets, and options.</para>
<para>To use the Mongo namespace elements you will need to reference the
Mongo schema:</para>
<example>
<title>XML schema to configure MongoDB</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
<emphasis role="bold">http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd</emphasis>
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;
<emphasis role="bold">&lt;mongo:mongo host="localhost" port="27017"/&gt;</emphasis>
&lt;/beans&gt;
</programlisting>
</example>
<para>A more advanced configuration with MongoOptions is shown below
(note these are not recommended values)</para>
<example>
<title>XML schema to configure a com.mongodb.Mongo object with
MongoOptions</title>
<programlisting language="xml">&lt;beans&gt;
&lt;mongo:mongo host="localhost" port="27017"&gt;
&lt;mongo:options connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="1000"
max-wait-time="1500}"
auto-connect-retry="true"
socket-keep-alive="true"
socket-timeout="1500"
slave-ok="true"
write-number="1"
write-timeout="0"
write-fsync="true"/&gt;
&lt;/mongo:mongo/&gt;
&lt;/beans&gt;
</programlisting>
</example>
<para>A configuration using replica sets is shown below. <example>
<title>XML schema to configure com.mongodb.Mongo object with Replica
Sets</title>
<programlisting language="xml">&lt;mongo:mongo id="replicaSetMongo" replica-set="127.0.0.1:27017,localhost:27018"/&gt; </programlisting>
</example></para>
</section>
<section>
<title>Registering a MongoDbFactory instance using Java based
metadata</title>
<para>As an alternative to configuring a
<classname>com.mongodb.Mongo</classname> instance and later providing
the database name and optionally the username and password, is to use
<classname>SimpleMongoDbFactory</classname>, which implements the
<interfacename>MongoDbFactory</interfacename> inteface shown below. A
reference to <interfacename>MongoDbFactory</interfacename> can be passed
to constructors of <classname>MongoTemplate</classname> to simplify its
creation.</para>
<programlisting language="java">public interface MongoDbFactory {
DB getDb() throws DataAccessException;
DB getDb(String dbName) throws DataAccessException;
}</programlisting>
<para>The most simple usage is shown below</para>
<programlisting>@Configuration
public class MongoConfiguration {
public @Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new Mongo(), "database");
}
}</programlisting>
<para>To define the username and password create an instance of
org.springframework.data.authentication.UserCredentials and pass it into
the constructor as shown below</para>
<programlisting>@Configuration
public class MongoConfiguration {
public @Bean MongoDbFactory mongoDbFactory() throws Exception {
UserCredentials userCredentials = new UserCredentials("joe", "secret");
return new SimpleMongoDbFactory(new Mongo(), "database", userCredentials);
}
}
</programlisting>
</section>
<section>
<title>Registering a Mongo instance using XML based metadata</title>
<para>The mongo namespace lets you create a MongoDbFactory instance
which is a convenient way to group together the a mongo instance, a
database name and an optional username and password. A simple usage is
shown below</para>
<programlisting language="xml"> &lt;mongo:db-factory dbname="database" &gt;</programlisting>
</section>
</section>
<section id="mongo-template">
<title>Introduction to MongoTemplate</title>
<para>The class <classname>MongoTemplate</classname>, located in the
package <literal>org.springframework.data.document.mongodb</literal>, is
the central class of the Spring's MongoDB support providng a rich feature
set. The template offers convenience operations to create, update, delete
and query for MongoDB document and provide a mapping between your domain
objects and MongoDB documents.</para>
<note>
<para>Once configured, <classname>MongoTemplate</classname> is
thread-safe and can be reused across multiple instances.</para>
</note>
<para>The mapping between Mongo documents and domain classes is done by
delegating to an implementation of the interface
<interfacename>MongoConverter</interfacename>. Spring provides two
implementations, <classname>SimpleMappingConverter</classname> and
<classname>MongoMappingConverter</classname>, but you can also write your
own converter. Please refer to the section on MongoCoverters for more
detailed information.</para>
<para>The <classname>MongoTemplate</classname> class implements the
interface <interfacename>MongoOperations</interfacename>. In as much as
possible, the methods on <interfacename>MongoOperations</interfacename>
are named after methods available on the MongoDB driver
<classname>Collection</classname> object. For example, you will find
methods such as "find", "findAndModify", "findOne", "insert", "remove",
"save", "update" and "updateMulti". The design goal was to make it as easy
as possible to transition between the use of the base MongoDB driver and
<interfacename>MongoOperations</interfacename>. The difference in betwee
the two is that MongOperations can be passed domain objects instead of
<classname>DBObject</classname> and there are fluent APIs for
<classname>Query</classname>, <classname>Criteria</classname>, and
<classname>Update</classname> operations instead of
<classname>DBObject</classname>..</para>
<note>
<para>The preferred way to reference the operations on
<classname>MongoTemplate</classname> instance is via its interface
<interfacename>MongoOperations</interfacename>.</para>
</note>
<para>The default converter implementation used by
<classname>MongoTemplate</classname> is
<classname>SimpleMappingConverter</classname>, which as the name implies,
is simple. <classname>SimpleMapingConverter</classname> does not use any
additional mapping metadata to converter a domain object to a MongoDB
document. As such, it does not support functionality such as DBRefs or
creating indexes using annotations on domain classes. For a detailed
description of the MongoMappingConverter read the section on <link
linkend="mongo.mapping">Mapping Support</link>.</para>
<para>Another central feature of MongoTemplate is exception translation of
exceptions thrown in the Mongo Java driver into Spring's portable Data
Access Exception hierarchy. Refer to the section on <link
linkend="mongo.exception">exception translation</link> for more
information.</para>
<para>While there are many convenience methods on
<classname>MongoTemplate</classname> to help you easily perform common
tasks if you should need to access the Mongo driver API directly to access
functionality not explicitly exposed by the MongoTemplate you can use one
of several Execute callback methods. These will give you a reference to a
Mongo Collection or DB object. Please see the section <ulink
url="mongo.executioncallback">Execution Callbacks</ulink> for more
information.</para>
<para>Now let's look at a examples of how to work with the
<classname>MongoTemplate</classname> in the context of the Spring
container.</para>
<section>
<title>Instantiating MongoTemplate</title>
<para>You can use Java to create and register an instance of
MongoTemplate as shown below.</para>
<example>
<title>Registering a com.mongodb.Mongo object and enabling Spring's
exception translation support</title>
<programlisting language="java">@Configuration
public class AppConfig {
public @Bean Mongo mongo() throws Exception {
return new Mongo("localhost");
}
public @Bean MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), "mydatabase", "mycollection");
}
}
</programlisting>
</example>
<para>There are several overloaded constructors of MongoTemplate. These
are</para>
<itemizedlist>
<listitem>
<para><emphasis role="bold">MongoTemplate </emphasis>
<literal>(Mongo mongo, String databaseName) </literal> - takes the
default database name to operate against</para>
</listitem>
<listitem>
<para><emphasis role="bold">MongoTemplate </emphasis>
<literal>(Mongo mongo, String databaseName, String
defaultCollectionName) </literal> - adds the default collection name
to operate against.</para>
</listitem>
<listitem>
<para><emphasis role="bold">MongoTemplate </emphasis>
<literal>(Mongo mongo, String databaseName, String
defaultCollectionName, MongoConverter mongoConverter) </literal> -
override with a provided MongoConverter. Default is
SimpleMongoConverter</para>
</listitem>
</itemizedlist>
<para>You can also configure a MongoTemplate using Spring's XML
&lt;beans/&gt; schema.</para>
<programlisting language="java"> &lt;mongo:mongo host="localhost" port="27017"/&gt;
&lt;bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate"&gt;
&lt;constructor-arg ref="mongo"/&gt;
&lt;constructor-arg name="databaseName" value="geospatial"/&gt;
&lt;constructor-arg name="defaultCollectionName" value="newyork"/&gt;
&lt;/bean&gt;</programlisting>
<para>Other properties that you might like to set when creating a
MongTemplate are WriteResultCheckingPolicy and the default
WriteConcern.</para>
<note>
<para>The preferred way to reference the operations on
<classname>MongoTemplate</classname> instance is via its interface
<interfacename>MongoOperations</interfacename>.</para>
</note>
<section>
<title>WriteResultChecking Policy</title>
<para>When in development it is very handy to either log or throw an
exception if the WriteResult returned from any MongoDB operation
contains an error. It is quite common to forget to do this during
development and then end up with an application that looks like it ran
successfully but the database was not modified according to your
expectations. Setting the WriteResultChecking is an enum with the
following values, NONE, LOG, EXCEPTION.</para>
<para>The default is to use a WriteResultChecking of NONE.</para>
</section>
<section>
<title>WriteConcern</title>
<para>You can set the WriteConcern property that the MongoTemplate
will use for write operations if it has not yet been specifid with the
driver. If not set, it will default to the one set in the MongoDB
driver's DB or Collection setting.</para>
<note>
<para>Setting the WriteConcern to different values when saving an
object will be provided in a future release. This will most likely
be handled using mapping metadata provided either in the form of
annotations on the domain object or by an external fluent
DSL.</para>
</note>
</section>
</section>
<section>
<title>Configuring the MongoConverter</title>
<para>The SimpleMongoConverter is used by default but if you want to use
the more feature rich MappingMongoConverter there are a few steps.
Please refer to the <link
linkend="mongodb.mapping-configuration">mapping section</link> for more
information.</para>
</section>
</section>
<section>
<title>Saving, Updating, and Removing Documents</title>
<para><classname>MongoTemplate</classname> provides a simple way for you
to save, update, and delete your domain objects and map those objects to
documents stored in MongoDB.</para>
<para>Given a simple class such as Person</para>
<programlisting language="java">public class Person {
private String id;
private String firstName;
private int age;
// getters and setter omitted
}</programlisting>
<para>You can save, update and delete the object as shown below.</para>
<note>
<para><interfacename>MongoOperations</interfacename> is the interface
that <classname>MongoTemplate</classname> implements.</para>
</note>
<programlisting language="java">public class PersonExample {
private static final Log log = LogFactory.getLog(PersonExample.class);
@Autowired
private MongoOperations mongoOps;
public void doWork() {
Person p = new Person();
p.setFirstName("Sven");
p.setAge(22);
// Save
mongoOps.save(p);
log.debug("Saved: " + p);
// Find
p = mongoOps.findOne(query(whereId().is(p.getId())), Person.class);
log.debug("Found: " + p);
// Update age to 24 for Sven
mongoOps.updateFirst(query(where("firstName").is("Sven")), update("age", 24));
p = mongoOps.findOne(query(whereId().is(p.getId())), Person.class);
log.debug("Updated: " + p);
// Delete
mongoOps.remove(p);
// Check that deletion worked
List&lt;Person&gt; people = mongoOps.getCollection(Person.class);
log.debug("Number of people = : " + people.size());
}</programlisting>
<para>This would produce the following log output (including some debug
message from <classname>MongoTemplate</classname> itself)</para>
<programlisting>Saved: PersonWithIdPropertyOfTypeString [id=4d9e82ac94fa72c65a9e7d5f, firstName=Sven, age=22]
findOne using query: { "_id" : { "$oid" : "4d9e82ac94fa72c65a9e7d5f"}} in db.collection: database.personexample
Found: PersonWithIdPropertyOfTypeString [id=4d9e82ac94fa72c65a9e7d5f, firstName=Sven, age=22]
findOne using query: { "_id" : { "$oid" : "4d9e82ac94fa72c65a9e7d5f"}} in db.collection: database.personexample
Updated: PersonWithIdPropertyOfTypeString [id=4d9e82ac94fa72c65a9e7d5f, firstName=Sven, age=24]
remove using query: { "_id" : { "$oid" : "4d9e82ac94fa72c65a9e7d5f"}}
Number of people = : 0</programlisting>
<para>There was implicit conversion using the MongoConverter between a
String and ObjectId as stored in the database and recognizing a convention
of the property "Id" name.</para>
<note>
<para>This example is meant to show the use of save, update and remove
operations on MongoTemplate and not to show complex mapping
functionality</para>
</note>
<para>The query stynax used in the example is explained in more detail in
the section <link linkend="mongo.query">Querying Documents</link>.</para>
<section>
<title>How the '_id' field is handled in the mapping layer</title>
<para>Mongo requires that you have an '_id' field for all documents. If
you don't provide one the driver will assign a ObjectId with a generated
value. When using the <classname>MongoMappingConverter</classname> there
are certain rules that govern how properties from the Java class is
mapped to this '_id' field.</para>
<para>The following outlines what property will be mapped to the '_id'
field:</para>
<para><itemizedlist>
<listitem>
<para>A property or field annotated with
<classname>@Id</classname>
(<classname>org.springframework.data.annotation.Id</classname>)
will be mapped to the '_id' field.</para>
</listitem>
<listitem>
<para>A property or field named <classname>id</classname> will be
mapped to the '_id' field.</para>
</listitem>
<listitem>
<para>A property or field declared as a String in the Java class
will be converted to and stored as an ObjectId if possible
(conversions and rules would be handled by the Mongo Java driver).
If it cannot be converted to an ObjectId, then the value will be
stored as a string in the database.</para>
</listitem>
<listitem>
<para>A property or field declared as anything but a String in the
Java class will be stored as the type it is declared as, which
means it must be one of the basic types supported by the Mongo
Java driver.</para>
</listitem>
</itemizedlist></para>
<para>If no field or property specified above is present in the Java
class then an implicit '_id' file will be generated by the driver but
not mapped to a property or field of the Java class.</para>
<para>When querying and updating the <classname>JdbcTemplate</classname>
will use the converter to handle conversions of the
<classname>Query</classname> and <classname>Update</classname> objects
that correspond to the above rules for saving documents so field names
and types used in your queries will be able to match what is in your
domain classes.</para>
</section>
<section>
<title>Methods for saving and inserting documents</title>
<para>There are several convenient methods on MongoTemplate for saving
and inserting your objects. In addition to using a
<interfacename>MongoCoverter</interfacename> to converter your domain
object to the database, you can also use an implementation of the
<interfacename>MongoWriter</interfacename> interface to have very fine
grained control over the conversion process.</para>
<note>
<para>The difference between insert and save operations is that a save
operation will perform an insert if the object is not already
present.</para>
</note>
<para>The simple case of using the save operation is to pass in as an
argument only the object to save. In this case the default collection
assigned to the template will be used unless the converter overrides
this default through the use of more specific mapping metadata. You may
also call the save operation with a specific collection name.</para>
<para>When inserting or saving, if the Id property is not set, the
assumption is that its value will be autogenerated by the database. As
such, for autogeneration of an ObjectId to succeed the type of the Id
property/field in your class must be either a
<classname>String</classname>, <classname>ObjectId</classname>, or
<classname>BigInteger</classname>.</para>
<para>Here is a basic example of using the save operation and retrieving
its contents.</para>
<example>
<title>Inserting and retrieving documents using the
MongoTemplate</title>
<programlisting language="java">import static org.springframework.data.document.mongodb.query.Criteria.where;
import static org.springframework.data.document.mongodb.query.Criteria.query;
...
Person p = new Person("Bob", 33);
mongoTemplate.insert("MyCollection", p);
Person qp = mongoTemplate.findOne("MyCollection", query(where("age").is(33)), Person.class);
</programlisting>
</example>
<para>The four save operations available to you are listed below.</para>
<para><itemizedlist>
<listitem>
<para><literal>void</literal> <emphasis role="bold">save
</emphasis> <literal>(Object objectToSave) </literal> Save the
object to the default collection.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis role="bold">save
</emphasis> <literal>(String collectionName, Object objectToSave)
</literal> Save the object to the specified collection.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis role="bold">save
</emphasis> <literal>(T objectToSave, MongoWriter&lt;T&gt; writer)
</literal> Save the object into the default collection using the
provided writer.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis role="bold">save
</emphasis> <literal>(String collectionName, T objectToSave,
MongoWriter&lt;T&gt; writer) </literal> Save the object into the
specified collection using the provided writer.</para>
</listitem>
</itemizedlist></para>
<para>A similar set of insert operations is listed below</para>
<para><itemizedlist>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">insert</emphasis> <literal>(Object objectToSave)
</literal> Insert the object to the default collection.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis role="bold">insert
</emphasis> <literal>(String collectionName, Object objectToSave)
</literal> Insert the object to the specified collection.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis
role="bold">insert </emphasis> <literal>(T objectToSave,
MongoWriter&lt;T&gt; writer) </literal> Insert the object into the
default collection using the provided writer.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis
role="bold">insert </emphasis> <literal>(String collectionName, T
objectToSave, MongoWriter&lt;T&gt; writer) </literal> Insert the
object into the specified collection using the provided
writer.</para>
</listitem>
</itemizedlist></para>
<para>Unless and explicit MongoWriter is passed into the save or insert
method, the the template's MongoConverter will be used.</para>
<section>
<title>Saving using MongoWriter</title>
<para>The MongoWriter interface allows you to have lower level control
over the mapping of an object into a DBObject. The MongoWriter
interface is</para>
<programlisting language="java">/**
* A MongoWriter is responsible for converting an object of type T to the native MongoDB
* representation DBObject.
*
* @param &lt;T&gt; the type of the object to convert to a DBObject
*/
public interface MongoWriter&lt;T&gt; {
/**
* Write the given object of type T to the native MongoDB object representation DBObject.
*
* @param t The object to convert to a DBObject
* @param dbo The DBObject to use for writing.
*/
void write(T t, DBObject dbo);
}</programlisting>
</section>
<section>
<title>Inserting Lists of objects in batch</title>
<para>The MongoDB driver supports inserting a collection of documents
in one operation. The methods in the MongoOperations interface that
support this functionality are listed below</para>
<para><itemizedlist>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">insertList</emphasis><literal> (List&lt;? extends
Object&gt; listToSave)</literal><literal> </literal> Insert a
list of objects into the default collection in a single batch
write to the database.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">insertList</emphasis> <literal>(String
collectionName, List&lt;? extends Object&gt;
listToSave)</literal><literal> </literal>Insert a list of
objects into the specified collection in a single batch write to
the database.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis
role="bold">insertList</emphasis><literal> (List&lt;? extends
T&gt; listToSave, MongoWriter&lt;T&gt; writer)</literal>
<literal></literal> Insert the object into the default
collection using the provided writer.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void</literal> <emphasis
role="bold">insertList</emphasis><literal>(String
collectionName, List&lt;? extends T&gt; listToSave,
MongoWriter&lt;T&gt; writer)</literal> Insert a list of objects
into the specified collection using the provided MongoWriter
instance</para>
</listitem>
</itemizedlist></para>
</section>
</section>
<section id="mongodb-template-update">
<title>Updating documents in a collection</title>
<para>For updates we can elect to update the first document found using
<interfacename>MongoOperation</interfacename>'s method
<literal>updateFirst</literal> or we can update all documents that were
found to match the query using the method
<literal>updateMulti</literal>. Here is an example of an update of all
SAVINGS accounts where we are adding a one time $50.00 bonus to the
balance using the <literal>$inc</literal> operator.</para>
<example>
<title>Updating documents using the MongoTemplate</title>
<programlisting language="java">import static org.springframework.data.document.mongodb.query.Criteria.where;
import static org.springframework.data.document.mongodb.query.Query.query;
import static org.springframework.data.document.mongodb.query.Update
...
WriteResult wr = mongoTemplate.updateMulti(query(where("accounts.accountType").is(Account.Type.SAVINGS)),
update.inc("accounts.$.balance", 50.00));
</programlisting>
</example>
<para>In addition to the <classname>Query</classname> discussed above we
provide the update definition using an <classname>Update</classname>
object. The <classname>Update</classname> class has methods that match
the update modifiers available for MongoDB.</para>
<para>As you can see most methods return the
<classname>Update</classname> object to provide a fluent style for the
API.</para>
<section>
<title>Methods for executing updates for documents</title>
<para><itemizedlist>
<listitem>
<para><literal>WriteResult</literal> <emphasis
role="bold">updateFirst </emphasis> <literal>(Query query,
Update update) </literal> Updates the first object that is found
in the default collection that matches the query document with
the provided updated document.</para>
</listitem>
<listitem>
<para><literal>WriteResult</literal> <emphasis
role="bold">updateFirst </emphasis> <literal>(String
collectionName, Query query, Update update) </literal> Updates
the first object that is found in the specified collection that
matches the query document criteria with the provided updated
document.</para>
</listitem>
<listitem>
<para><literal>WriteResult</literal> <emphasis
role="bold">updateMulti </emphasis> <literal>(Query query,
Update update) </literal> Updates all objects that are found in
the default collection that matches the query document criteria
with the provided updated document.</para>
</listitem>
<listitem>
<para><literal>WriteResult</literal> <emphasis
role="bold">updateMulti </emphasis> <literal>(String
collectionName, Query query, Update update) </literal> Updates
all objects that are found in the specified collection that
matches the query document criteria with the provided updated
document.</para>
</listitem>
</itemizedlist></para>
<para></para>
</section>
<section>
<title>Methods for the Update class</title>
<para><itemizedlist>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">addToSet
</emphasis> <literal>(String key, Object value) </literal>
Update using the <literal>$addToSet</literal> update
modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">inc
</emphasis> <literal>(String key, Number inc) </literal> Update
using the <literal>$inc</literal> update modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">pop
</emphasis> <literal>(String key, Update.Position pos)
</literal> Update using the <literal>$pop</literal> update
modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">pull
</emphasis> <literal>(String key, Object value) </literal>
Update using the <literal>$pull</literal> update modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">pullAll
</emphasis> <literal>(String key, Object[] values) </literal>
Update using the <literal>$pullAll</literal> update
modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">push
</emphasis> <literal>(String key, Object value) </literal>
Update using the <literal>$push</literal> update modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">pushAll
</emphasis> <literal>(String key, Object[] values) </literal>
Update using the <literal>$pushAll</literal> update
modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">rename
</emphasis> <literal>(String oldName, String newName) </literal>
Update using the <literal>$rename</literal> update
modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">set
</emphasis> <literal>(String key, Object value) </literal>
Update using the <literal>$set</literal> update modifier</para>
</listitem>
<listitem>
<para><literal>Update</literal> <emphasis role="bold">unset
</emphasis> <literal>(String key)</literal> Update using the
<literal>$unset</literal> update modifier</para>
</listitem>
</itemizedlist></para>
<para></para>
</section>
</section>
<section>
<title>Methods for removing documents</title>
<para>You can use several overloaded methods to remove an object from
the database.</para>
<para><itemizedlist>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">remove</emphasis> <literal>(Object object)</literal>
Remove the given object from the collection by Id</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">remove</emphasis> <literal>(Query query)</literal>
Remove all documents from the default collection that match the
provided query document criteria.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis role="bold">remove
</emphasis> <literal>(String collectionName, Query query)
</literal> Remove all documents from the specified collection that
match the provided query document criteria.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void </literal><emphasis
role="bold">remove</emphasis> <literal>(Query query,
Class&lt;T&gt; targetClass)</literal> Same behavior as the
remove(Query) method but the Class parameter is used to help
convert the Id of the object if it is present in the query.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; void </literal><emphasis
role="bold">remove</emphasis> <literal>(String collectionName,
Query query, Class&lt;T&gt; targetClass)</literal>Same behavior as
remove(Query, Class) but the Class parameter is used to help
convert the Id of the object if it is present in the uqery</para>
</listitem>
</itemizedlist></para>
<para></para>
</section>
</section>
<section id="mongo.query">
<title>Querying Documents</title>
<para>You can express you queries using the Query and Criteria classes
which have method names that mirror the native MongoDB operator names such
as lt, lte, is, and others. The Query and Criteria classes follow a fluent
API style so that you can easily chain together multiple method criteria
and queries while having easy to understand code. Static imports in Java
are used to help remove the need to see the 'new' keyword for creating
Query and Criteria instances so as to improve readability.</para>
<para>GeoSpatial queries are also supported and are described more in the
section <link linkend="mongo.geospatial">GeoSpatial Queries</link>.</para>
<section id="mongodb-template-query">
<title>Querying documents in a collection</title>
<para>We saw how to retrieve a single document. We can also query for a
collection of documents to be returned as domain objects in a list.
Assuming that we have a number of Person objects with name and age
stored as documents in a collection and that each person has an embedded
account document with a balance. We can now run a query using the
following code.</para>
<example>
<title>Querying for documents using the MongoTemplate</title>
<programlisting language="java">import static org.springframework.data.document.mongodb.query.Criteria.where;
import static org.springframework.data.document.mongodb.query.Query.query;
...
List&lt;Person&gt; result = mongoTemplate.find(query(where("age").lt(50).and("accounts.balance").gt(1000.00d)),Person.class);
</programlisting>
</example>
<para>All find methods take a <classname>Query</classname> object as a
parameter. This object defines the criteria and options used to perform
the query. The criteria is specified using a
<classname>Criteria</classname> object that has a static factory method
named <classname>where</classname> used to instantiate a new
<classname>Criteria</classname> object. We recommend using a static
import for
<classname>org.springframework.data.document.mongodb.query.Criteria.where</classname>
and <literal>Query.query</literal> to make the query more
readable.</para>
<para>This query should return a list of Person objects that meet the
specified criteria. The Criteria class has the following methods that
correspond to the operators provided in MongoDB.</para>
<para>As you can see most methods return the
<classname>Criteria</classname> object to provide a fluent style for the
API.</para>
<section>
<title>Methods for the Criteria class</title>
<para>
<itemizedlist>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">all
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$all</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis
role="bold">elemMatch </emphasis> <literal>(Criteria c)
</literal>Creates a criterion using the
<literal>$elemMatch</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">exists
</emphasis> <literal>(boolean b) </literal>Creates a criterion
using the <literal>$exists</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">gt
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$gt</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">gte
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$gte</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">in
</emphasis> <literal>(Object... o) </literal>Creates a criterion
using the <literal>$in</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">is
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$is</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">lt
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$lt</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">lte
</emphasis> <literal>(Object o)</literal>Creates a criterion
using the <literal>$lte</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">mod
</emphasis> <literal>(Number value, Number
remainder)</literal>Creates a criterion using the
<literal>$mod</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">nin
</emphasis> <literal>(Object... o) </literal>Creates a criterion
using the <literal>$nin</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">not
</emphasis> <literal>()</literal>Creates a criterion using the
<literal>$not</literal> meta operator which affects the clause
directly following</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">regex
</emphasis> <literal>(String re) </literal>Creates a criterion
using a <literal>$regex</literal></para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">size
</emphasis> <literal>(int s)</literal>Creates a criterion using
the <literal>$size</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">type
</emphasis> <literal>(int t)</literal>Creates a criterion using
the <literal>$type</literal> operator</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">and
</emphasis> <literal>(String key) </literal>Adds a chained
<classname>Criteria</classname> with the specified
<literal>key</literal> to the current
<classname>Criteria</classname> and retuns the newly created
one</para>
</listitem>
</itemizedlist>
</para>
</section>
<para>There are also methods on the Criteria class for geospatial
queries. Here is al isting but look at the section on <link
linkend="mongo.geospatial">GeoSpatial Queries</link> to see them in
action.</para>
<itemizedlist>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">withinCenter
</emphasis> <literal>(Circle circle)</literal>Creates a geospatial
criterion using <literal>$within $center</literal> operators</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis
role="bold">withinCenterSphere </emphasis> <literal>(Circle circle)
</literal>Creates a geospatial criterion using <literal>$within
$center</literal> operators. This is only available for Mongo 1.7
and higher.</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">withinBox
</emphasis> <literal>(Box box)</literal>Creates a geospatial
criterion using a <literal>$within $box</literal> operation
<literal /></para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">near
</emphasis> <literal>(Point point)</literal>Creates a geospatial
criterion using a <literal>$near </literal>operation</para>
</listitem>
<listitem>
<para><literal>Criteria</literal> <emphasis role="bold">nearSphere
</emphasis> <literal>(Point point) </literal>Creates a geospatial
criterion using <literal>$nearSphere$center</literal> operations.
This is only available for Mongo 1.7 and higher.</para>
</listitem>
</itemizedlist>
<para>The <classname>Query</classname> class has some additional methods
used to provide options for the query.</para>
<section>
<title>Methods for the Query class</title>
<para>
<itemizedlist>
<listitem>
<para><literal>Query</literal> <emphasis role="bold">addCriteria
</emphasis> <literal>(Criteria criteria)</literal> used to add
additional criteria to the query</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis
role="bold">or</emphasis> <literal>(List&lt;Query&gt;
queries)</literal> Creates an or query using the
<literal>$or</literal> operator for all of the provided
queries</para>
</listitem>
<listitem>
<para><literal>Field</literal> <emphasis role="bold">fields
</emphasis> <literal>()</literal> used to define fields to be
included in the query results</para>
</listitem>
<listitem>
<para><literal>Query</literal> <emphasis role="bold">limit
</emphasis> <literal>(int limit)</literal> used to limit the
size of the returned results to the provided limit (used for
paging)</para>
</listitem>
<listitem>
<para><literal>Query</literal> <emphasis role="bold">skip
</emphasis> <literal>(int skip)</literal> used to skip the
provided number of documents in the results (used for
paging)</para>
</listitem>
<listitem>
<para><literal>Sort</literal> <emphasis role="bold">sort
</emphasis> <literal>()</literal> used to provide sort
definition for the results</para>
<para />
</listitem>
</itemizedlist>
</para>
</section>
</section>
<section>
<title>Methods for querying for documents</title>
<para><itemizedlist>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">getCollection </emphasis> <literal>(Class&lt;T&gt;
targetClass) </literal> Query for a list of objects of type T from
the default collection.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">getCollection </emphasis> <literal>(String
collectionName, Class&lt;T&gt; targetClass) </literal> Query for a
list of objects of type T from the specified collection.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">getCollection </emphasis> <literal>(String
collectionName, Class&lt;T&gt; targetClass, MongoReader&lt;T&gt;
reader) </literal> Query for a list of objects of type T from the
specified collection, mapping the DBObject using the provided
MongoReader.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">findOne
</emphasis> <literal>(Query query, Class&lt;T&gt; targetClass)
</literal> Map the results of an ad-hoc query on the default
MongoDB collection to a single instance of an object of the
specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">findOne
</emphasis> <literal>(Query query, Class&lt;T&gt; targetClass,
MongoReader&lt;T&gt; reader) </literal> Map the results of an
ad-hoc query on the default MongoDB collection to a single
instance of an object of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">findOne
</emphasis> <literal>(String collectionName, Query query,
Class&lt;T&gt; targetClass) </literal> Map the results of an
ad-hoc query on the specified collection to a single instance of
an object of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">findOne
</emphasis> <literal>(String collectionName, Query query,
Class&lt;T&gt; targetClass, MongoReader&lt;T&gt; reader)
</literal> Map the results of an ad-hoc query on the specified
collection to a single instance of an object of the specified
type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">find </emphasis> <literal>(Query query, Class&lt;T&gt;
targetClass) </literal> Map the results of an ad-hoc query on the
default MongoDB collection to a List of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">find </emphasis> <literal>(Query query, Class&lt;T&gt;
targetClass, MongoReader&lt;T&gt; reader) </literal> Map the
results of an ad-hoc query on the default MongoDB collection to a
List of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">find </emphasis> <literal>(String collectionName,
Query query, Class&lt;T&gt; targetClass) </literal> Map the
results of an ad-hoc query on the specified collection to a List
of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">find </emphasis> <literal>(String collectionName,
Query query, Class&lt;T&gt; targetClass, CursorPreparer preparer)
</literal> Map the results of an ad-hoc query on the specified
collection to a List of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; List&lt;T&gt;</literal> <emphasis
role="bold">find </emphasis> <literal>(String collectionName,
Query query, Class&lt;T&gt; targetClass, MongoReader&lt;T&gt;
reader) </literal> Map the results of an ad-hoc query on the
specified collection to a List of the specified type.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis
role="bold">findAndRemove </emphasis> <literal>(Query query,
Class&lt;T&gt; targetClass) </literal> Map the results of an
ad-hoc query on the default MongoDB collection to a single
instance of an object of the specified type. The first document
that matches the query is returned and also removed from the
collection in the database.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis
role="bold">findAndRemove </emphasis> <literal>(Query query,
Class&lt;T&gt; targetClass, MongoReader&lt;T&gt; reader)
</literal> Map the results of an ad-hoc query on the default
MongoDB collection to a single instance of an object of the
specified type. The first document that matches the query is
returned and also removed from the collection in the
database.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis
role="bold">findAndRemove </emphasis> <literal>(String
collectionName, Query query, Class&lt;T&gt; targetClass)
</literal> Map the results of an ad-hoc query on the specified
collection to a single instance of an object of the specified
type. The first document that matches the query is returned and
also removed from the collection in the database.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis
role="bold">findAndRemove </emphasis> <literal>(String
collectionName, Query query, Class&lt;T&gt; targetClass,
MongoReader&lt;T&gt; reader) </literal> Map the results of an
ad-hoc query on the specified collection to a single instance of
an object of the specified type. The first document that matches
the query is returned and also removed from the collection in the
database.</para>
</listitem>
</itemizedlist></para>
<para>The MongoReader can be used</para>
<section>
<title>Reading using MongoWriter</title>
<para>The MongoReader interface allows you to have lower level control
over the mapping of an DBObject into a Java object. This is similar to
the role of RowMapper in JdbcTemplate. The MongoReader interface
is</para>
<programlisting language="java">/**
* A MongoWriter is responsible for converting a native MongoDB DBObject to an object of type T.
*
* @param &lt;T&gt; the type of the object to convert from a DBObject
*/
public interface MongoReader&lt;T&gt; {
/**
* Ready from the native MongoDB DBObject representation to an instance of the class T.
* The given type has to be the starting point for marshalling the {@link DBObject}
* into it. So in case there's no real valid data inside {@link DBObject} for the
* given type, just return an empty instance of the given type.
*
* @param clazz the type of the return value
* @param dbo theDBObject
* @return the converted object
*/
&lt;S extends T&gt; S read(Class&lt;S&gt; clazz, DBObject dbo);
}</programlisting>
<para></para>
<para></para>
</section>
</section>
<section id="mongo.geospatial" lang="">
<title>GeoSpatial Queries</title>
<para>MongoDB supports GeoSpatial queries through the use of operators
such as <literal>$near</literal>, <literal>$within</literal>, and
<literal>$nearSphere</literal>. Methods specific to geospatial queries
are available on the <classname>Criteria</classname> class. There are
also a few shape classes, <classname>Box</classname>,
<classname>Circle</classname>, and <classname>Point</classname> that are
used in conjunction with geospatial related Criteria methods.</para>
<para>To understand how to perform GeoSpatial queries we will use the
following Venue class taken from the integration tests.which relies on
using the rich <classname>MappingMongoConverter</classname>.</para>
<programlisting language="java">@Document(collection="newyork")
public class Venue {
@Id
private String id;
private String name;
private double[] location;
@PersistenceConstructor
Venue(String name, double[] location) {
super();
this.name = name;
this.location = location;
}
public Venue(String name, double x, double y) {
super();
this.name = name;
this.location = new double[] { x, y };
}
public String getName() {
return name;
}
public double[] getLocation() {
return location;
}
@Override
public String toString() {
return "Venue [id=" + id + ", name=" + name + ", location="
+ Arrays.toString(location) + "]";
}
}</programlisting>
<para>To find locations within a circle, the following query can be
used.</para>
<programlisting lang="" language="java">Circle circle = new Circle(-73.99171, 40.738868, 0.01);
List&lt;Venue&gt; venues =
template.find(new Query(Criteria.where("location").withinCenter(circle)), Venue.class);</programlisting>
<para>To find venues within a circle using Spherical coordinates the
following query can be used</para>
<programlisting lang="" language="java">Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784);
List&lt;Venue&gt; venues =
template.find(new Query(Criteria.where("location").withinCenterSphere(circle)), Venue.class);</programlisting>
<para>To find venues within a Box the following query can be used</para>
<programlisting language="java">//lower-left then upper-right
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404));
List&lt;Venue&gt; venues =
template.find(new Query(Criteria.where("location").withinBox(box)), Venue.class);</programlisting>
<para>To find venues near a Point, the following query can be
used</para>
<programlisting language="java">Point point = new Point(-73.99171, 40.738868);
List&lt;Venue&gt; venues =
template.find(new Query(Criteria.where("location").near(point).maxDistance(0.01)), Venue.class);</programlisting>
<para>To find venues near a Point using Spherical coordines the
following query can be used</para>
<programlisting language="java">Point point = new Point(-73.99171, 40.738868);
List&lt;Venue&gt; venues =
template.find(new Query(
Criteria.where("location").nearSphere(point).maxDistance(0.003712240453784)),
Venue.class);
</programlisting>
<note>
<para>Support for the GeoNear command will be provided in the RC
release.</para>
</note>
</section>
</section>
<section>
<title>Index and Collection managment</title>
<para>MongoTemplate provides a few methods for managing indexes and
collections.</para>
<section>
<title>Methods for creating an Index</title>
<para>We can create an index on a collection to improve query
performance.</para>
<example>
<title>Creating an index using the MongoTemplate</title>
<programlisting language="java">mongoTemplate.ensureIndex("MyCollection", new Index().on("name",Order.ASCENDING)); </programlisting>
</example>
<para><itemizedlist>
<listitem>
<para><literal>void</literal> <emphasis role="bold">ensureIndex
</emphasis> <literal>(IndexDefinition indexDefintion) </literal>
Ensure that an index for the provided IndexDefinition exists for
the default collection.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis role="bold">ensureIndex
</emphasis> <literal>(String collectionName, IndexDefinition
indexSpecification) </literal> Ensure that an index for the
provided IndexDefinition exists.</para>
</listitem>
</itemizedlist></para>
<para>You can create both standard indexes and geospatial indexes using
the classes <classname>IndexDefinition</classname> and
<classname>GeoSpatialIndex</classname> respectfully. For example, given
the Venue class defined in a previous section, you would declare a
geospatial query as shown below</para>
<programlisting language="java">mongoTemplate.ensureIndex(new GeospatialIndex("location"));</programlisting>
</section>
<section>
<title>Methods for working with a Collection</title>
<para>It's time to look at some code examples showing how to use the
<classname>MongoTemplate</classname>. First we look at creating our
first collection.</para>
<example>
<title>Working with collections using the MongoTemplate</title>
<programlisting language="java">DBCollection collection = null;
if (!mongoTemplate.getCollectionNames().contains("MyNewCollection")) {
collection = mongoTemplate.createCollection("MyNewCollection");
}
mongoTemplate.dropCollection("MyNewCollection"); </programlisting>
</example>
<para><itemizedlist>
<listitem>
<para><literal>Set&lt;String&gt;</literal> <emphasis
role="bold">getCollectionNames </emphasis> <literal>()</literal> A
set of collection names.</para>
</listitem>
<listitem>
<para><literal>boolean</literal> <emphasis
role="bold">collectionExists </emphasis> <literal>(String
collectionName) </literal> Check to see if a collection with a
given name exists.</para>
</listitem>
<listitem>
<para><literal>DBCollection</literal> <emphasis
role="bold">createCollection </emphasis> <literal>(String
collectionName) </literal> Create an uncapped collection with the
provided name.</para>
</listitem>
<listitem>
<para><literal>DBCollection</literal> <emphasis
role="bold">createCollection </emphasis> <literal>(String
collectionName, CollectionOptions collectionOptions) </literal>
Create a collect with the provided name and options.</para>
</listitem>
<listitem>
<para><literal>void</literal> <emphasis role="bold">dropCollection
</emphasis> <literal>(String collectionName) </literal> Drop the
collection with the given name.</para>
</listitem>
<listitem>
<para><literal>DBCollection</literal> <emphasis
role="bold">getCollection </emphasis> <literal>(String
collectionName) </literal> Get a collection by name, creating it
if it doesn't exist.</para>
</listitem>
<listitem>
<para><literal>DBCollection</literal> <emphasis
role="bold">getDefaultCollection </emphasis> <literal>()</literal>
The default collection used by this template.</para>
</listitem>
<listitem>
<para><literal>String</literal> <emphasis
role="bold">getDefaultCollectionName </emphasis>
<literal>()</literal> The default collection name used by this
template.</para>
</listitem>
</itemizedlist></para>
<para></para>
</section>
</section>
<section>
<title>Executing Commands</title>
<para>You can also get at the Mongo driver's collection.command( ) method
using the executeCommand methods on MongoTemplate. These will also perform
exception translation into Spring's Data Access Exception
hierarchy.</para>
<section>
<title>Methods for executing commands</title>
<para><itemizedlist>
<listitem>
<para><literal>CommandResult</literal> <emphasis
role="bold">executeCommand </emphasis> <literal>(DBObject command)
</literal> Execute a MongoDB command.</para>
</listitem>
<listitem>
<para><literal>CommandResult</literal> <emphasis
role="bold">executeCommand </emphasis> <literal>(String
jsonCommand) </literal> Execute the a MongoDB command expressed as
a JSON string.</para>
</listitem>
</itemizedlist></para>
<para></para>
</section>
</section>
<section id="mongodb.mapping-usage.events">
<title>Lifecycle Events</title>
<para>Built into the MongoDB mapping framework are several
<classname>org.springframework.context.ApplicationEvent</classname> events
that your application can respond to by registering special beans in the
<code>ApplicationContext</code>. By being based off Spring's
ApplicationContext event infastructure this enables other products, such
as Spring Integration, to easily receive these events as they are a well
known eventing mechanism in Spring based applications.</para>
<para>To intercept an object before it goes through the conversion process
(which turns your domain object into a
<classname>com.mongodb.DBObject</classname>), you'd register a subclass of
<classname>org.springframework.data.document.mongodb.mapping.event.AbstractMappingEventListener</classname>
that overrides the <code>onBeforeConvert</code> method. When the event is
dispatched, your listener will be called and passed the domain object
before it goes into the converter.</para>
<example>
<programlisting language="java">
public class BeforeConvertListener&lt;BeforeConvertEvent, Person&gt; extends AbstractMappingEventListener {
@Override
public void onBeforeConvert(Person p) {
... does some auditing manipulation, set timestamps, whatever ...
}
}
</programlisting>
</example>
<para>To intercept an object before it goes into the database, you'd
register a subclass of
<classname>org.springframework.data.document.mongodb.mapping.event.AbstractMappingEventListener</classname>
that overrides the <code>onBeforeSave</code> method. When the event is
dispatched, your listener will be called and passed the domain object and
the converted <classname>com.mongodb.DBObject</classname>.</para>
<example>
<programlisting language="java">
public class BeforeSaveListener&lt;BeforeSaveEvent, Person&gt; extends AbstractMappingEventListener {
@Override
public void onBeforeSave(Person p, DBObject dbo) {
... change values, delete them, whatever ...
}
}
</programlisting>
</example>
<para>Simply declaring these beans in your Spring ApplicationContext will
cause them to be invoked whenever the event is dispatched.</para>
<para>The list of callback methods that are present in
AbstractMappingEventListener are</para>
<itemizedlist>
<listitem>
<para><methodname>onBeforeConvert</methodname> - called in
MongoTemplate insert, insertList and save operations before the object
is converted to a DBObject using a MongoConveter.</para>
</listitem>
<listitem>
<para><methodname>onBeforeSave</methodname> - called in MongoTemplate
insert, insertList and save operations <emphasis>before</emphasis>
inserting/saving the DBObject in the database.</para>
</listitem>
<listitem>
<para><methodname>onAfterSave</methodname> - called in MongoTemplate
insert, insertList and save operations <emphasis>after</emphasis>
inserting/saving the DBObject in the database.</para>
</listitem>
<listitem>
<para><methodname>onAfterLoad</methodname> - called in MongoTempnlate
find, findAndRemove, findOne and getCollection methods after the
DBObject is retrieved from the database.</para>
</listitem>
<listitem>
<para><methodname>onAfterConvert</methodname> - called in
MongoTempnlate find, findAndRemove, findOne and getCollection methods
after the DBObject retrieved from the database was converted to a
POJO.</para>
</listitem>
</itemizedlist>
</section>
<section id="mongo.exception" label="">
<title>Exception Translation</title>
<para>The Spring framework provides exception translation for a wide
variety of database and mapping technologies. This has traditionally been
for JDBC and JPA. The Spring support for Mongo extends this feature to the
MongoDB Database by providing an implementation of the
<classname>org.springframework.dao.support.PersistenceExceptionTranslator</classname>
interface.</para>
<para>The motivation behind mapping to Spring's <ulink
url="http://static.springsource.org/spring/docs/3.0.x/reference/dao.html#dao-exceptions">consistent
data access exception hierarchy</ulink> is that you are then able to write
portable and descriptive exception handling code without resorting to
coding against <ulink
url="http://www.mongodb.org/display/DOCS/Error+Codes">MongoDB error
codes</ulink>. All of Spring's data access exceptions are inherited from
the root <classname>DataAccessException</classname> class so you can be
sure that you will be able to catch all database related exception within
a single try-catch block. Note, that not all exceptions thrown by the
MongoDB driver inherit from the MongoException class. The inner exception
and message are preserved so no information is lost.</para>
<para>Some of the mappings performed by the MongoExceptionTranslator are:
com.mongodb.Network to DataAccessResourceFailureException and
MongoException error codes 1003, 12001, 12010, 12011, 12012 to
InvalidDataAccessApiUsageException. Look into the implementation for more
details on the mapping.</para>
</section>
<section id="mongo.executioncallback">
<title>Execution Callback</title>
<para>One common design feature of all Spring template classes is that all
functionality is routed into one of the templates execute callback
methods. This helps ensure that exceptions and any resource management
that maybe required are performed consistency. While this was of much
greater need in the case of JDBC and JMS than with MongoDB, it still
offers a single spot for exception translation and logging to occur. As
such, using thexe execute callback is the preferred way to access the
Mongo driver's DB and Collection objects to perform uncommon operations
that were not exposed as methods on
<classname>MongoTemplate</classname>.</para>
<para>Here is a list of execute callback methods.</para>
<para><itemizedlist>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">execute
</emphasis> <literal>(CollectionCallback&lt;T&gt; action) </literal>
Executes the given CollectionCallback on the default
collection.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">execute
</emphasis> <literal>(String collectionName,
CollectionCallback&lt;T&gt; action) </literal> Executes the given
CollectionCallback on the collection of the given name.update using
the $addToSet update modifier</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis role="bold">execute
</emphasis> <literal>(DbCallback&lt;T&gt; action) </literal>
Executes a DbCallback translating any exceptions as
necessary.</para>
</listitem>
<listitem>
<para><literal>&lt;T&gt; T</literal> <emphasis
role="bold">executeInSession </emphasis>
<literal>(DbCallback&lt;T&gt; action) </literal> Executes the given
DbCallback within the same connection to the database so as to
ensure consistency in a write heavy environment where you may read
the data that you wrote.</para>
</listitem>
</itemizedlist></para>
<para>Here is an example that uses the CollectionCallback to return
information about an index.</para>
<programlisting language="java"> boolean hasIndex = template.execute("geolocation", new CollectionCallback&lt;Boolean&gt;() {
public Boolean doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List&lt;DBObject&gt; indexes = collection.getIndexInfo();
for (DBObject dbo : indexes) {
if ("location_2d".equals(dbo.get("name"))) {
return true;
}
}
return false;
}
});</programlisting>
</section>
</chapter>