DATADOC-59 - Document @Query annotation for repositories

DATADOC-82 - Document QueryDslPredicateExecutor
This commit is contained in:
Mark Pollack
2011-04-07 01:25:36 -04:00
parent e946782259
commit 1c3483abbf
8 changed files with 838 additions and 748 deletions

View File

@@ -81,7 +81,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
private String replacePlaceholders(String input, ConvertingParameterAccessor accessor) {
Matcher matcher = PLACEHOLDER.matcher(input);
String result = null;
String result = input;
while (matcher.find()) {
String group = matcher.group();

View File

@@ -84,6 +84,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
List<Person> result = repository.findByThePersonsFirstname("Leroi");
assertThat(result.size(), is(1));
assertThat(result, hasItem(leroi));
assertThat(result.get(0).getAge(),is(nullValue()));
}
@Test

View File

@@ -44,7 +44,7 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
* @param firstname
* @return
*/
@Query("{ 'firstname' : ?0 }")
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname': 1, 'lastname': 1}")
List<Person> findByThePersonsFirstname(String firstname);

View File

@@ -62,6 +62,8 @@
<xi:include href="reference/mongodb.xml"/>
<xi:include href="reference/mongo-repositories.xml"/>
<xi:include href="reference/mapping.xml"/>
<xi:include href="reference/cross-store.xml"/>
<xi:include href="reference/logging.xml"/>
<xi:include href="reference/jmx.xml"/>
</part>

View File

@@ -0,0 +1,17 @@
<?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.cross.store">
<title>Cross Store support</title>
<para>Cross Store support introduction.
</para>
<section id="mongodb:croos-store-configuration">
<title>Cross Store Configuration</title>
<para>Cross Store...
</para>
</section>
</chapter>

View File

@@ -0,0 +1,17 @@
<?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.logging">
<title>Logging support</title>
<para>Logging support introduction.
</para>
<section id="mongodb:logging-configuration">
<title>MongoDB Log4j Configuration</title>
<para>Log4j...
</para>
</section>
</chapter>

View File

@@ -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&lt;Person, Long&gt; {
<programlisting>public interface PersonRepository extends MongoRepository&lt;Person, Long&gt; {
}
// 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">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<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: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"&gt;
&lt;mongo:repositories base-package="com.acme.*.repositories"
mongo-template-ref="myMongoTemplate" /&gt;
&lt;mongo:mongo id="mongo" /&gt;
&lt;bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate"&gt;
&lt;constructor-arg ref="mongo" /&gt;
&lt;constructor-arg value="database" /&gt;
&lt;constructor-arg value="collection" /&gt;
&lt;constructor-arg&gt;
&lt;mongo:mapping-converter /&gt;
&lt;/constructor-arg&gt;
&lt;/bean&gt;
&lt;mongo:repositories base-package="com.acme.*.repositories" mongo-template-ref="myMongoTemplate" /&gt;
&lt;/beans&gt;</programlisting>
</example>
&lt;/beans&gt;</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&lt;Person&gt; 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&lt;Person, Long&gt; {
<programlisting>public interface PersonRepository extends MongoRepository&lt;Person, String&gt; {
List&lt;Person&gt; findByLastname(String lastname);
List&lt;Person&gt; findByLastname(String lastname);
Page&lt;Person&gt; findByFirstname(String firstname, Pageable pageable);
}
</programlisting>
Page&lt;Person&gt; 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&lt;Person, String&gt;
@Query("{ 'firstname' : ?0 }")
List&lt;Person&gt; 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&lt;Person, String&gt;
@Query(value="{ 'firstname' : ?0 }", fields="firstname,lastname")
List&lt;Person&gt; 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&lt;Person&gt; result = repository.findAll(person.address.zipCode.eq("C0123"));
Page&lt;Person&gt; 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&lt;T&gt; {
T findOne(Predicate predicate);
List&lt;T&gt; findAll(Predicate predicate);
List&lt;T&gt; findAll(Predicate predicate, OrderSpecifier&lt;?&gt;... orders);
Page&lt;T&gt; 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&lt;Person, String&gt;, QueryDslPredicateExecutor&lt;Person&gt; {
// additional finder methods go here
}</programlisting>
</section>
</section>
</chapter>
</chapter>

File diff suppressed because it is too large Load Diff