|
|
|
|
@@ -1,70 +1,68 @@
|
|
|
|
|
<?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.repositories">
|
|
|
|
|
<title>Mongo repositories</title>
|
|
|
|
|
|
|
|
|
|
<abstract>
|
|
|
|
|
<para>This chapter will point out the specialties for repository support
|
|
|
|
|
for MongoDB. This builds on the core repository support explained in<xref
|
|
|
|
|
linkend="repositories"/>. So make sure you've got a sound understanding
|
|
|
|
|
of the basic concepts explained there.
|
|
|
|
|
</para>
|
|
|
|
|
</abstract>
|
|
|
|
|
<section id="mongo-repo-intro">
|
|
|
|
|
<title>Introduction</title>
|
|
|
|
|
|
|
|
|
|
<para>To access domain entities stored in a MongoDB you can leverage our
|
|
|
|
|
<para>This chapter will point out the specialties for repository support
|
|
|
|
|
for MongoDB. This builds on the core repository support explained in<xref
|
|
|
|
|
linkend="repositories" />. So make sure you've got a sound understanding
|
|
|
|
|
of the basic concepts explained there.</para>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section id="mongo-repo-usage">
|
|
|
|
|
<title>Usage</title>
|
|
|
|
|
|
|
|
|
|
<para>To access domain entities stored in a MongoDB you can leverage our
|
|
|
|
|
sophisticated repository support that eases implementing those quite
|
|
|
|
|
significantly. To do so, simply create an interface for your
|
|
|
|
|
repository:
|
|
|
|
|
</para>
|
|
|
|
|
repository:</para>
|
|
|
|
|
|
|
|
|
|
<example>
|
|
|
|
|
<title>Sample Person entity</title>
|
|
|
|
|
<example>
|
|
|
|
|
<title>Sample Person entity</title>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">public class Person {
|
|
|
|
|
<programlisting language="java">public class Person {
|
|
|
|
|
|
|
|
|
|
private ObjectId id;
|
|
|
|
|
private String id;
|
|
|
|
|
private String firstname;
|
|
|
|
|
private String lastname;
|
|
|
|
|
private Address address;
|
|
|
|
|
|
|
|
|
|
// … getters and setters omitted
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
</example>
|
|
|
|
|
|
|
|
|
|
<para>We have a quite simple domain object here. Note that it has a property
|
|
|
|
|
named
|
|
|
|
|
<code>id</code>
|
|
|
|
|
of type<classname>ObjectId</classname>. The default
|
|
|
|
|
serialization mechanism used in
|
|
|
|
|
<classname>MongoTemplate</classname>
|
|
|
|
|
(which
|
|
|
|
|
is backing the repository support) regards properties named id as document
|
|
|
|
|
id. Currently we support<classname>String</classname>,
|
|
|
|
|
<classname>ObjectId</classname>
|
|
|
|
|
and
|
|
|
|
|
<classname>BigInteger</classname>
|
|
|
|
|
as
|
|
|
|
|
id-types.
|
|
|
|
|
</para>
|
|
|
|
|
<para>We have a quite simple domain object here. Note that it has a
|
|
|
|
|
property named <code>id</code> of type<classname>ObjectId</classname>. The
|
|
|
|
|
default serialization mechanism used in
|
|
|
|
|
<classname>MongoTemplate</classname> (which is backing the repository
|
|
|
|
|
support) regards properties named id as document id. Currently we
|
|
|
|
|
support<classname>String</classname>, <classname>ObjectId</classname> and
|
|
|
|
|
<classname>BigInteger</classname> as id-types.</para>
|
|
|
|
|
|
|
|
|
|
<example>
|
|
|
|
|
<title>Basic repository interface to persist Person entities</title>
|
|
|
|
|
<example>
|
|
|
|
|
<title>Basic repository interface to persist Person entities</title>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, Long> {
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, Long> {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// additional custom finder methods go here
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
</example>
|
|
|
|
|
|
|
|
|
|
<para>The central MongoDB CRUD repository interface is
|
|
|
|
|
<para>The central MongoDB CRUD repository interface is
|
|
|
|
|
<interfacename>MongoRepository</interfacename>. Right now this interface
|
|
|
|
|
simply serves typing purposes but we will add additional methods to it
|
|
|
|
|
later. In your Spring configuration simply add
|
|
|
|
|
</para>
|
|
|
|
|
later. In your Spring configuration simply add</para>
|
|
|
|
|
|
|
|
|
|
<example>
|
|
|
|
|
<title>General mongo repository Spring configuration</title>
|
|
|
|
|
<example>
|
|
|
|
|
<title>General mongo repository Spring configuration</title>
|
|
|
|
|
|
|
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<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:mongo="http://www.springframework.org/schema/data/mongo"
|
|
|
|
|
@@ -76,117 +74,109 @@
|
|
|
|
|
http://www.springframework.org/schema/context
|
|
|
|
|
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
|
|
|
|
|
|
|
|
|
<mongo:repositories base-package="com.acme.*.repositories"
|
|
|
|
|
mongo-template-ref="myMongoTemplate" />
|
|
|
|
|
<mongo:mongo id="mongo" />
|
|
|
|
|
|
|
|
|
|
<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
|
|
|
|
|
<constructor-arg ref="mongo" />
|
|
|
|
|
<constructor-arg value="database" />
|
|
|
|
|
<constructor-arg value="collection" />
|
|
|
|
|
<constructor-arg>
|
|
|
|
|
<mongo:mapping-converter />
|
|
|
|
|
</constructor-arg>
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
…
|
|
|
|
|
<mongo:repositories base-package="com.acme.*.repositories" mongo-template-ref="myMongoTemplate" />
|
|
|
|
|
…
|
|
|
|
|
|
|
|
|
|
</beans></programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
</beans></programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
|
|
|
|
|
<para>This namespace element will cause the base packages to be scanned for
|
|
|
|
|
interfaces extending
|
|
|
|
|
<interfacename>MongoRepository</interfacename>
|
|
|
|
|
and
|
|
|
|
|
create Spring beans for each of them found. By default the repositories will
|
|
|
|
|
get a
|
|
|
|
|
<classname>MongoTemplate</classname>
|
|
|
|
|
Spring bean wired that is called
|
|
|
|
|
<code>mongoTemplate</code>, so you only need to configure
|
|
|
|
|
<code>mongo-template-ref</code>
|
|
|
|
|
explicitly if you deviate from this
|
|
|
|
|
convention.
|
|
|
|
|
</para>
|
|
|
|
|
<para>This namespace element will cause the base packages to be scanned
|
|
|
|
|
for interfaces extending <interfacename>MongoRepository</interfacename>
|
|
|
|
|
and create Spring beans for each of them found. By default the
|
|
|
|
|
repositories will get a <classname>MongoTemplate</classname> Spring bean
|
|
|
|
|
wired that is called <code>mongoTemplate</code>, so you only need to
|
|
|
|
|
configure <code>mongo-template-ref</code> explicitly if you deviate from
|
|
|
|
|
this convention.</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<interfacename>MongoRepository</interfacename>
|
|
|
|
|
extends
|
|
|
|
|
<interfacename>PagingAndSortingRepository</interfacename>
|
|
|
|
|
which you can read
|
|
|
|
|
about in<xref linkend="repositories.repository"/>. In general it provides
|
|
|
|
|
you with CRUD operations as well as methods for paginated and sorted access
|
|
|
|
|
to the entities. Working with the repository instance is just a matter of
|
|
|
|
|
dependency injecting it into a client. So accessing the second page of
|
|
|
|
|
<classname>Person</classname>s at a page size of 10 would simply look
|
|
|
|
|
something like this:
|
|
|
|
|
</para>
|
|
|
|
|
<para><interfacename>MongoRepository</interfacename> extends
|
|
|
|
|
<interfacename>PagingAndSortingRepository</interfacename> which you can
|
|
|
|
|
read about in<xref linkend="repositories.repository" />. In general it
|
|
|
|
|
provides you with CRUD operations as well as methods for paginated and
|
|
|
|
|
sorted access to the entities. Working with the repository instance is
|
|
|
|
|
just a matter of dependency injecting it into a client. So accessing the
|
|
|
|
|
second page of <classname>Person</classname>s at a page size of 10 would
|
|
|
|
|
simply look something like this:</para>
|
|
|
|
|
|
|
|
|
|
<example>
|
|
|
|
|
<title>Paging access to Person entities</title>
|
|
|
|
|
<example>
|
|
|
|
|
<title>Paging access to Person entities</title>
|
|
|
|
|
|
|
|
|
|
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
class PersonRepositoryTests {
|
|
|
|
|
<programlisting>@RunWith(SpringJUnit4ClassRunner.class)
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
public class PersonRepositoryTests {
|
|
|
|
|
|
|
|
|
|
@Autowired PersonRepository repository;
|
|
|
|
|
@Autowired PersonRepository repository;
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void readsFirstPageCorrectly() {
|
|
|
|
|
@Test
|
|
|
|
|
public void readsFirstPageCorrectly() {
|
|
|
|
|
|
|
|
|
|
Page<Person> persons = repository.findAll(new PageRequest(0, 10));
|
|
|
|
|
assertThat(persons.isFirstPage(), is(true));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
}
|
|
|
|
|
} </programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
|
|
|
|
|
<para>The sample creates an application context with Spring's unit test
|
|
|
|
|
<para>The sample creates an application context with Spring's unit test
|
|
|
|
|
support which will perform annotation based dependency injection into test
|
|
|
|
|
cases. Inside the test method we simply use the repository to query the
|
|
|
|
|
datastore. We hand the repository a
|
|
|
|
|
<classname>PageRequest</classname>
|
|
|
|
|
datastore. We hand the repository a <classname>PageRequest</classname>
|
|
|
|
|
instance that requests the first page of persons at a page size of
|
|
|
|
|
10.
|
|
|
|
|
</para>
|
|
|
|
|
10.</para>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section id="mongodb.repositories.queries">
|
|
|
|
|
<title>Query methods</title>
|
|
|
|
|
|
|
|
|
|
<para>Most of the data access operations you usually trigger on a
|
|
|
|
|
repository result a query being executed against the Mongo databases.
|
|
|
|
|
Defining such a query is just a matter of declaring a method on the
|
|
|
|
|
repository interface
|
|
|
|
|
</para>
|
|
|
|
|
repository result a query being executed against the Mongo databases.
|
|
|
|
|
Defining such a query is just a matter of declaring a method on the
|
|
|
|
|
repository interface</para>
|
|
|
|
|
|
|
|
|
|
<example>
|
|
|
|
|
<title>PersonRepository with query methods</title>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, Long> {
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, String> {
|
|
|
|
|
|
|
|
|
|
List<Person> findByLastname(String lastname);
|
|
|
|
|
List<Person> findByLastname(String lastname);
|
|
|
|
|
|
|
|
|
|
Page<Person> findByFirstname(String firstname, Pageable pageable);
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
Page<Person> findByFirstname(String firstname, Pageable pageable);
|
|
|
|
|
|
|
|
|
|
Person findByShippingAddresses(Address address);
|
|
|
|
|
|
|
|
|
|
} </programlisting>
|
|
|
|
|
</example>
|
|
|
|
|
|
|
|
|
|
<para>The first method shows a query for all people with the given
|
|
|
|
|
lastname. The query will be derived parsing the method name for
|
|
|
|
|
constraints which can be concatenated with
|
|
|
|
|
<literal>And</literal>
|
|
|
|
|
and
|
|
|
|
|
<literal>Or</literal>. Thus the method name will result in a query
|
|
|
|
|
expression of<code>{"lastname" : lastname}</code>. The second example
|
|
|
|
|
shows how pagination is applied to a query. Just equip your method
|
|
|
|
|
signature with a
|
|
|
|
|
<interfacename>Pageable</interfacename>
|
|
|
|
|
parameter and let
|
|
|
|
|
the method return a
|
|
|
|
|
<interfacename>Page</interfacename>
|
|
|
|
|
instance and we
|
|
|
|
|
will automatically page the query accordingly.
|
|
|
|
|
</para>
|
|
|
|
|
lastname. The query will be derived parsing the method name for
|
|
|
|
|
constraints which can be concatenated with <literal>And</literal> and
|
|
|
|
|
<literal>Or</literal>. Thus the method name will result in a query
|
|
|
|
|
expression of<code>{"lastname" : lastname}</code>. The second example
|
|
|
|
|
shows how pagination is applied to a query. Just equip your method
|
|
|
|
|
signature with a <interfacename>Pageable</interfacename> parameter and let
|
|
|
|
|
the method return a <interfacename>Page</interfacename> instance and we
|
|
|
|
|
will automatically page the query accordingly. The third examples shows
|
|
|
|
|
that you can query based on properties which are not a primitive
|
|
|
|
|
type.</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<table>
|
|
|
|
|
<para><table>
|
|
|
|
|
<title>Supported keywords for query methods</title>
|
|
|
|
|
|
|
|
|
|
<tgroup cols="3">
|
|
|
|
|
<colspec colwidth="1*"/>
|
|
|
|
|
<colspec colwidth="1*" />
|
|
|
|
|
|
|
|
|
|
<colspec colwidth="2*"/>
|
|
|
|
|
<colspec colwidth="2*" />
|
|
|
|
|
|
|
|
|
|
<colspec colwidth="2*"/>
|
|
|
|
|
<colspec colwidth="2*" />
|
|
|
|
|
|
|
|
|
|
<thead>
|
|
|
|
|
<row>
|
|
|
|
|
@@ -200,133 +190,186 @@
|
|
|
|
|
|
|
|
|
|
<tbody>
|
|
|
|
|
<row>
|
|
|
|
|
<entry>
|
|
|
|
|
<literal>GreaterThan</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><literal>GreaterThan</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByAgeGreaterThan(int
|
|
|
|
|
age)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByAgeGreaterThan(int age)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : {"$gt" : age}}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : {"$gt" : age}}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>
|
|
|
|
|
<literal>LessThan</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><literal>LessThan</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByAgeLessThan(int
|
|
|
|
|
age)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByAgeLessThan(int age)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : {"$lt" : age}}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : {"$lt" : age}}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>
|
|
|
|
|
<literal>Between</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><literal>Between</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByAgeBetween(int from, int
|
|
|
|
|
to)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByAgeBetween(int from, int to)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : {"$gt" : from, "$lt" : to}}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : {"$gt" : from, "$lt" : to}}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry><literal>IsNotNull</literal>,
|
|
|
|
|
<literal>NotNull</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<literal>NotNull</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByFirstnameNotNull()</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByFirstnameNotNull()</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : {"$ne" : null}}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : {"$ne" : null}}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry><literal>IsNull</literal>,
|
|
|
|
|
<literal>Null</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<literal>Null</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByFirstnameNull()</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByFirstnameNull()</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : null}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : null}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>
|
|
|
|
|
<literal>Like</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><literal>Like</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByFirstnameLike(String
|
|
|
|
|
name)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByFirstnameLike(String name)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : age}</code>
|
|
|
|
|
(
|
|
|
|
|
<varname>age</varname>
|
|
|
|
|
as
|
|
|
|
|
regex)
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : age}</code> ( <varname>age</varname> as
|
|
|
|
|
regex)</entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>(No keyword)</entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByFirstname(String
|
|
|
|
|
name)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByFirstname(String name)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : name}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : name}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>
|
|
|
|
|
<literal>Not</literal>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><literal>Not</literal></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<methodname>findByFirstnameNot(String
|
|
|
|
|
name)
|
|
|
|
|
</methodname>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><methodname>findByFirstnameNot(String name)
|
|
|
|
|
</methodname></entry>
|
|
|
|
|
|
|
|
|
|
<entry>
|
|
|
|
|
<code>{"age" : {"$ne" : name}}</code>
|
|
|
|
|
</entry>
|
|
|
|
|
<entry><code>{"age" : {"$ne" : name}}</code></entry>
|
|
|
|
|
</row>
|
|
|
|
|
</tbody>
|
|
|
|
|
</tgroup>
|
|
|
|
|
</table>
|
|
|
|
|
</para>
|
|
|
|
|
</table></para>
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<title>Mongo JSON based query methods and field restriction </title>
|
|
|
|
|
|
|
|
|
|
<para>The annotation
|
|
|
|
|
org.springframework.data.document.mongodb.repository.Query can be
|
|
|
|
|
applied to finder methods on your repository which allows you to specify
|
|
|
|
|
a Mongo JSON string to define the actual query to be executed instead of
|
|
|
|
|
infering the query from the method name. For example</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, String>
|
|
|
|
|
|
|
|
|
|
@Query("{ 'firstname' : ?0 }")
|
|
|
|
|
List<Person> findByThePersonsFirstname(String firstname);
|
|
|
|
|
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The placeholder ?0 lets you substitue the value from the method
|
|
|
|
|
arguments into the JSON query definition.</para>
|
|
|
|
|
|
|
|
|
|
<para>You can also use the filter property to define the set of
|
|
|
|
|
properties that will be mapped into the Java object. For example,
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, String>
|
|
|
|
|
|
|
|
|
|
@Query(value="{ 'firstname' : ?0 }", fields="firstname,lastname")
|
|
|
|
|
List<Person> findByThePersonsFirstname(String firstname);
|
|
|
|
|
|
|
|
|
|
}</programlisting>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<title>Type-safe Query methods</title>
|
|
|
|
|
|
|
|
|
|
<para>Mongo repository support integrates with the <ulink
|
|
|
|
|
url="http://www.querydsl.com/">QueryDSL</ulink> project which provides a
|
|
|
|
|
meant to perform type-safe queries (no strings) in Java. To quote from
|
|
|
|
|
the project description, "Instead of writing queries as inline strings
|
|
|
|
|
or externalizing them into XML files they are constructed via a fluent
|
|
|
|
|
API." It provides the following features</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Code completion in IDE (all properties, methods and operations
|
|
|
|
|
can be expanded in your favorite Java IDE)</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Almost no syntactically invalid queries allowed (type-safe on
|
|
|
|
|
all levels)</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Domain types and properties can be referenced safely (no
|
|
|
|
|
Strings involved!)</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Adopts better to refactoring changes in domain types</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>Incremental query definition is easier </para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>This will enable you to write queries as shown below</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>QPerson person = new QPerson("person");
|
|
|
|
|
List<Person> result = repository.findAll(person.address.zipCode.eq("C0123"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Page<Person> page = repository.findAll(person.lastname.contains("a"),
|
|
|
|
|
new PageRequest(0, 2, Direction.ASC, "lastname"));</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>QPerson is a class that is generated (via the Java annotation post
|
|
|
|
|
processing tool) which is a Predicate that allows you to write type safe
|
|
|
|
|
queries. Notice that there are no strings in the query other than the
|
|
|
|
|
value "C0123".</para>
|
|
|
|
|
|
|
|
|
|
<para>The use of the generated Predicate class is made available via the
|
|
|
|
|
interface QueryDslPredicateExecutor which is shown below</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface QueryDslPredicateExecutor<T> {
|
|
|
|
|
|
|
|
|
|
T findOne(Predicate predicate);
|
|
|
|
|
|
|
|
|
|
List<T> findAll(Predicate predicate);
|
|
|
|
|
|
|
|
|
|
List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
|
|
|
|
|
|
|
|
|
|
Page<T> findAll(Predicate predicate, Pageable pageable);
|
|
|
|
|
|
|
|
|
|
Long count(Predicate predicate);
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>To use this in your repository implementation, simply inherit from
|
|
|
|
|
it as well. This is shown below</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>public interface PersonRepository extends MongoRepository<Person, String>, QueryDslPredicateExecutor<Person> {
|
|
|
|
|
|
|
|
|
|
// additional finder methods go here
|
|
|
|
|
|
|
|
|
|
}</programlisting>
|
|
|
|
|
</section>
|
|
|
|
|
</section>
|
|
|
|
|
</chapter>
|
|
|
|
|
</chapter>
|
|
|
|
|
|