diff --git a/pom.xml b/pom.xml index c2972dae5..2ea00357f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 1.0.0.BUILD-SNAPSHOT jar 2012 - http://github.com/biomedcentral/spring-data-elasticsearch + https://github.com/BioMedCentralLtd/spring-data-elasticsearch Spring Data Elasticsearch Spring Data module providing support for Elasticsearch repositories @@ -56,10 +56,6 @@ - https://github.com/biomedcentral/spring-data-elasticsearch @@ -68,10 +64,10 @@ - + + Travis + https://travis-ci.org/BioMedCentralLtd/spring-data-elasticsearch + @@ -312,7 +308,7 @@ - - - - - + + + distribute + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + + + javadoc + + package + + + + true +
Spring Data Elasticsearch
+ ${project.version} + true + + true + + http://static.springframework.org/spring/docs/3.1.x/javadoc-api + http://download.oracle.com/javase/1.5.0/docs/api + http://static.springsource.org/spring-data/data-commons/docs/${spring.data-commons.version}/api + +
+
+ + com.agilejava.docbkx + docbkx-maven-plugin + 2.0.7 + + + + generate-html + generate-pdf + + package + + + + + org.docbook + docbook-xml + 4.4 + runtime + + + + index.xml + true + ${project.basedir}/src/docbkx/resources/xsl/fopdf.xsl + css/html.css + false + ${project.basedir}/src/docbkx/resources/xsl/html.xsl + 1 + 1 + + + version + ${project.version} + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + maven-assembly-plugin + 2.2.1 + + + distribution + + single + + package + + + ${project.basedir}/src/main/assembly/distribution.xml + + false + + + + +
+
+
+ - - - - - - - - - - - - - - - - - + + + biomedcentral + BioMed Central Development Team + +0 + + - - - - - - diff --git a/src/docbkx/index.xml b/src/docbkx/index.xml index 57fb60946..14690c759 100644 --- a/src/docbkx/index.xml +++ b/src/docbkx/index.xml @@ -3,25 +3,17 @@ "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> - Spring Data Solr + Spring Data Elasticsearch - Christoph - Strobl + Rizwan + Idrees - Oliver - Gierke + Mohsin + Husen - - Mark - Pollack - - - Thomas - Risberg - - + Copies of this document may be made for your own use and for @@ -35,7 +27,7 @@ - 2012 + 2013 The original author(s) @@ -51,7 +43,7 @@ - + diff --git a/src/docbkx/preface.xml b/src/docbkx/preface.xml index 056adf7e2..e9d859e59 100644 --- a/src/docbkx/preface.xml +++ b/src/docbkx/preface.xml @@ -3,13 +3,13 @@ "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> Preface - The Spring Data Solr project applies core Spring concepts to + The Spring Data Elasticsearch project applies core Spring concepts to the - development of solutions using the Apache Solr Search Engine. - We - provide a "template" as a high-level abstraction for storing and - querying documents. You will notice similarities to the mongodb - support in the Spring Framework. + development of solutions using the Elasticsearch Search Engine. + We have povided a "template" as a high-level abstraction for + storing,querying,sorting and faceting documents. You will notice similarities + to the Spring data solr and + mongodb support in the Spring Framework.
Project Metadata @@ -17,56 +17,18 @@ Version Control - - git://github.com/SpringSource/spring-data-solr.git + git://github.com/BioMedCentralLtd/spring-data-elasticsearch.git - - - Bugtacker - - https://jira.springsource.org/browse/DATASOLR - - - - - - Release repository - - http://repo.springsource.org/libs-release - - - - - - Milestone repository - - http://repo.springsource.org/libs-milestone - - - - - - Snapshot repository - - http://repo.springsource.org/libs-snapshot - -
Requirements Requires - Apache Solr - 3.5 and above or optional dependency - <dependency> - <groupId>org.apache.solr</groupId> - <artifactId>solr-core</artifactId> - <version>${solr.version}</version> -</dependency> - - If you tend to use the Embedded Version of Solr - Server 4.x you will also have to add a version of servlet-api and - check your <lockType> as well as <unlockOnStartup> - settings. - - + Elasticsearch + 0.20.2 and above or optional dependency +
\ No newline at end of file diff --git a/src/docbkx/reference/data-elasticsearch.xml b/src/docbkx/reference/data-elasticsearch.xml new file mode 100644 index 000000000..7a9f573f7 --- /dev/null +++ b/src/docbkx/reference/data-elasticsearch.xml @@ -0,0 +1,599 @@ + + + + Solr Repositories + + This chapter includes details of the Solr repository + implementation. + + +
+ Introduction + +
+ Spring Namespace + + + The Spring Data Elasticsearch module contains a custom namespace allowing + definition of repository beans as well as elements for instantiating + a + ElasticsearchServer + . + + + + Using the + repositories + element looks up Spring Data repositories as described in + + . + + + + Setting up Elasticsearch repositories using Namespace + <?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:elasticsearch="http://www.springframework.org/schema/data/elasticsearch" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/elasticsearch + http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"> + + <elasticsearch:repositories base-package="com.acme.repositories" /> +</beans> + + + + Using the + Transport Client + or + Node Client + element registers an instance of + Elasticsearch Server + in the context. + + + Transport Client using Namespace + <?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:elasticsearch="http://www.springframework.org/schema/data/elasticsearch" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/elasticsearch + http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"> + + <elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" /> +</beans> + + + + Node Client using Namespace + <?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:elasticsearch="http://www.springframework.org/schema/data/elasticsearch" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/elasticsearch + http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd"> + + <elasticsearch:node-client id="client" local="true"" /> +</beans> + + +
+
+ Annotation based configuration + The Spring Data Elasticsearch repositories support cannot only be + activated through an XML namespace but also using an annotation + through JavaConfig. + + + Spring Data Elasticsearch repositories using JavaConfig + @Configuration + @EnableElasticsearchRepositories(basePackages = "org/springframework/data/elasticsearch/repositories") + static class Config { + + @Bean + public ElasticsearchOperations elasticsearchTemplate() { + return new ElasticsearchTemplate(nodeBuilder().local(true).node().client()); + } + } + + The configuration above sets up an + Embedded Elasticsearch Server + which is used by the + ElasticsearchTemplate + . Spring Data Elasticsearch Repositories are activated using the + @EnableElasticsearchRepositories + annotation, which + essentially carries the same attributes as the XML + namespace does. If no + base package is configured, it will use the + one + the configuration class + resides in. + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Query methods +
+ Query lookup strategies + + The Elasticsearch module supports all basic query building feature as String,Abstract,Criteria or + have + it being derived from the method name. + + There is no QueryDSL Support present at this time. + + + + + Declared queries + + Deriving the query from the method name is not always sufficient + and/or may result in unreadable method names. In this case one + might make either use of Elasticsearch named queries (see + + ) or use the + @Query + annotation (see + + ). + + +
+ +
+ Query creation + + + Generally the query creation mechanism for Elasticsearch works as described + in + + . Here's a short example + of what a Elasticsearch query method translates into: + + Query creation from method names + public interface BookRepository extends Repository<Book, String> { + List<Book> findByNameAndPrice(String name, Integer price); +} + + The method name above will be translated into the following + Elasticsearch json query + + { + "bool" : { + "must" : [ { + "field" : { + "type" : "test" + } + }, { + "field" : { + "message" : "some message" + } + } ] + } + } + + + + A list of supported keywords for Elasticsearch is shown below. + + Supported keywords inside method names + + + + + + + Keyword + Sample + Elasticsearch Query String + + + + + + And + + + findByNameAndPrice + + + { + "bool" : { + "must" : [ { + "field" : { + "name" : "?" + } + }, { + "field" : { + "price" : "?" + } + } ] + } + } + + + + + Or + + + findByNameOrPrice + + + { + "bool" : { + "should" : [ { + "field" : { + "name" : "?" + } + }, { + "field" : { + "price" : "?" + } + } ] + } + } + + + + + Is + + + findByName + + + { + "bool" : { + "must" : { + "field" : { + "name" : "?" + } + } + } + } + + + + + Not + + + findByNameNot + + + { + "bool" : { + "must_not" : { + "field" : { + "name" : "?" + } + } + } + } + + + + + Between + + + findByPriceBetween + + + { + "bool" : { + "must" : { + "range" : { + "price" : { + "from" : ?, + "to" : ?, + "include_lower" : true, + "include_upper" : true + } + } + } + } + } + + + + + LessThanEqual + + + findByPriceLessThan + + + { + "bool" : { + "must" : { + "range" : { + "price" : { + "from" : null, + "to" : ?, + "include_lower" : true, + "include_upper" : true + } + } + } + } + } + + + + + GreaterThanEqual + + + findByPriceGreaterThan + + + { + "bool" : { + "must" : { + "range" : { + "rate" : { + "from" : ?, + "to" : null, + "include_lower" : true, + "include_upper" : true + } + } + } + } + } + + + + + Before + + + findByPriceBefore + + + { + "bool" : { + "must" : { + "range" : { + "price" : { + "from" : null, + "to" : ?, + "include_lower" : true, + "include_upper" : true + } + } + } + } + } + + + + + After + + + findByLastModifiedAfter + + + q=last_modified:[?0 TO *] + + + + + Like + + + findByNameLike + + + q=name:?0* + + + + + StartingWith + + + findByNameStartingWith + + + q=name:?0* + + + + + EndingWith + + + findByNameEndingWith + + + q=name:*?0 + + + + + Containing + + + findByNameContaining + + + q=name:*?0* + + + + + In + + + findByNameIn(Collection<String> + names) + + + + q=name:(?0... ) + + + + + NotIn + + + findByNameNotIn(Collection<String> + names) + + + + q=-name:(?0... ) + + + + + Near + + + findByStoreNear + + + q={!geofilt pt=?0.latitude,?0.longitude sfield=store + d=?1} + + + + + + True + + + findByAvailableTrue + + + q=inStock:true + + + + + False + + + findByAvailableFalse + + + q=inStock:false + + + + + OrderBy + + + findByAvailableTrueOrderByNameDesc + + + q=inStock:true&sort=name desc + + + + +
+
+
+
+ Using @Query Annotation + + Using named queries ( + + ) to declare queries for entities is a valid + approach and works fine + for a small number of queries. As the + queries themselves are tied to + the Java method that executes them, + you actually can bind them + directly using the Spring Data Solr + @Query + annotation. + + + + Declare query at the method using the + <interfacename>@Query</interfacename> + annotation. + + + public interface ProductRepository extends SolrRepository<Product, String> { + @Query("inStock:?0") + List<Product> findByAvailable(Boolean available); +} + +
+
+ Using NamedQueries + + Named queries can be kept in a properties file and wired to the + accroding method. Please mind the naming convention described in + + or use + @Query + . + + + + Declare named query in properites file + + Product.findByNamedQuery=popularity:?0 +Product.findByName=name:?0 + public interface ProductRepository extends SolrCrudRepository<Product, String> { + + List<Product> findByNamedQuery(Integer popularity); + + @Query(name = "Product.findByName") + List<Product> findByAnnotatedNamedQuery(String name); + +} + +
+
+
\ No newline at end of file diff --git a/src/docbkx/reference/data-solr.xml b/src/docbkx/reference/data-solr.xml deleted file mode 100644 index a5659e0ad..000000000 --- a/src/docbkx/reference/data-solr.xml +++ /dev/null @@ -1,501 +0,0 @@ - - - - Solr Repositories - - This chapter includes details of the Solr repository - implementation. - - -
- Introduction - -
- Spring Namespace - - - The Spring Data Solr module contains a custom namespace allowing - definition of repository beans as well as elements for instantiating - a - SolrServer - . - - - - Using the - repositories - element looks up Spring Data repositories as described in - - . - - - - Setting up Solr repositories using Namespace - <?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:solr="http://www.springframework.org/schema/data/solr" - xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/data/solr - http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd"> - - <solr:repositories base-package="com.acme.repositories" /> -</beans> - - - - Using the - solr-server - or - embedded-solr-server - element registers an instance of - SolrServer - in the context. - - - HttpSolrServer using Namespace - <?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:solr="http://www.springframework.org/schema/data/solr" - xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/data/solr - http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd"> - - <solr:solr-server id="solrServer" url="http://locahost:8983/solr" /> -</beans> - - - - EmbeddedSolrServer using Namespace - <?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:solr="http://www.springframework.org/schema/data/solr" - xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/data/solr - http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd"> - - <solr:embedded-solr-server id="solrServer" solrHome="classpath:com/acme/solr" /> -</beans> - - -
-
- Annotation based configuration - The Spring Data Solr repositories support cannot only be - activated through an XML namespace but also using an annotation - through JavaConfig. - - - Spring Data Solr repositories using JavaConfig - @Configuration -@EnableSolrRepositories -class ApplicationConfig { - - @Bean - public SolrServer solrServer() { - EmbeddedSolrServerFactory factory = new EmbeddedSolrServerFactory("classpath:com/acme/solr"); - return factory.getSolrServer(); - } - - @Bean - public SolrOperations solrTemplate() { - return new SolrTemplate(solrServer()); - } -} - - The configuration above sets up an - EmbeddedSolrServer - which is used by the - SolrTemplate - . Spring Data Solr Repositories are activated using the - @EnableSolrRepositories - annotation, which - essentially carries the same attributes as the XML - namespace does. If no - base package is configured, it will use the - one - the configuration class - resides in. - - -
-
- Solr Repositores using CDI - The Spring Data Solr repositories can also be set up using CDI - functionality. - - - Spring Data Solr repositories using JavaConfig - class SolrTemplateProducer { - - @Produces - @ApplicationScoped - public SolrOperations createSolrTemplate() { - return new SolrTemplate(new EmbeddedSolrServerFactory("classpath:com/acme/solr")); - } -} - -class ProductService { - - private ProductRepository repository; - - public Page<Product> findAvailableProductsByName(String name, Pageable pageable) { - return repository.findByAvailableTrueAndNameStartingWith(name, pageable); - } - - @Inject - public void setRepository(ProductRepository repository) { - this.repository = repository; - } -} - -
-
-
- Query methods -
- Query lookup strategies - - The Solr module supports defining a query manually as String or - have - it being derived from the method name. - - There is no QueryDSL Support present at this time. - - - - - Declared queries - - Deriving the query from the method name is not always sufficient - and/or may result in unreadable method names. In this case one - might make either use of Solr named queries (see - - ) or use the - @Query - annotation (see - - ). - - -
- -
- Query creation - - - Generally the query creation mechanism for Solr works as described - in - - . Here's a short example - of what a Solr query method translates into: - - Query creation from method names - public interface ProductRepository extends Repository<Product, String> { - List<Product> findByNameAndPopularity(String name, Integer popularity); -} - - The method name above will be translated into the following - solr query - - q=name:?0 AND popularity:?1 - - - - A list of supported keywords for Solr is shown below. - - Supported keywords inside method names - - - - - - - Keyword - Sample - Solr Query String - - - - - - And - - - findByNameAndPopularity - - - q=name:?0 AND popularity:?1 - - - - - Or - - - findByNameOrPopularity - - - q=name:?0 OR popularity:?1 - - - - - Is - - - findByName - - - q=name:?0 - - - - - Not - - - findByNameNot - - - q=-name:?0 - - - - - Between - - - findByPopularityBetween - - - q=popularity:[?0 TO ?1] - - - - - LessThanEqual - - - findByPopularityLessThan - - - q=popularity:[* TO ?0] - - - - - GreaterThanEqual - - - findByPopularityGreaterThan - - - q=popularity:[?0 TO *] - - - - - Before - - - findByLastModifiedBefore - - - q=last_modified:[* TO ?0] - - - - - After - - - findByLastModifiedAfter - - - q=last_modified:[?0 TO *] - - - - - Like - - - findByNameLike - - - q=name:?0* - - - - - StartingWith - - - findByNameStartingWith - - - q=name:?0* - - - - - EndingWith - - - findByNameEndingWith - - - q=name:*?0 - - - - - Containing - - - findByNameContaining - - - q=name:*?0* - - - - - In - - - findByNameIn(Collection<String> - names) - - - - q=name:(?0... ) - - - - - NotIn - - - findByNameNotIn(Collection<String> - names) - - - - q=-name:(?0... ) - - - - - Near - - - findByStoreNear - - - q={!geofilt pt=?0.latitude,?0.longitude sfield=store - d=?1} - - - - - - True - - - findByAvailableTrue - - - q=inStock:true - - - - - False - - - findByAvailableFalse - - - q=inStock:false - - - - - OrderBy - - - findByAvailableTrueOrderByNameDesc - - - q=inStock:true&sort=name desc - - - - -
-
-
-
- Using @Query Annotation - - Using named queries ( - - ) to declare queries for entities is a valid - approach and works fine - for a small number of queries. As the - queries themselves are tied to - the Java method that executes them, - you actually can bind them - directly using the Spring Data Solr - @Query - annotation. - - - - Declare query at the method using the - <interfacename>@Query</interfacename> - annotation. - - - public interface ProductRepository extends SolrRepository<Product, String> { - @Query("inStock:?0") - List<Product> findByAvailable(Boolean available); -} - -
-
- Using NamedQueries - - Named queries can be kept in a properties file and wired to the - accroding method. Please mind the naming convention described in - - or use - @Query - . - - - - Declare named query in properites file - - Product.findByNamedQuery=popularity:?0 -Product.findByName=name:?0 - public interface ProductRepository extends SolrCrudRepository<Product, String> { - - List<Product> findByNamedQuery(Integer popularity); - - @Query(name = "Product.findByName") - List<Product> findByAnnotatedNamedQuery(String name); - -} - -
-
-
\ No newline at end of file diff --git a/src/docbkx/reference/repositories.xml b/src/docbkx/reference/repositories.xml new file mode 100644 index 000000000..e9bd00853 --- /dev/null +++ b/src/docbkx/reference/repositories.xml @@ -0,0 +1,1111 @@ + + + + Repositories + +
+ Introduction + + Implementing a data access layer of an application has been + cumbersome for quite a while. Too much boilerplate code had to be written. + Domain classes were anemic and not designed in a real object oriented or + domain driven manner. + + Using both of these technologies makes developers life a lot easier + regarding rich domain model's persistence. Nevertheless the amount of + boilerplate code to implement repositories especially is still quite high. + So the goal of the repository abstraction of Spring Data is to reduce the + effort to implement data access layers for various persistence stores + significantly. + + The following chapters will introduce the core concepts and + interfaces of Spring Data repositories in general for detailled + information on the specific features of a particular store consult the + later chapters of this document. + + + As this part of the documentation is pulled in from Spring Data + Commons we have to decide for a particular module to be used as example. + The configuration and code samples in this chapter are using the JPA + module. Make sure you adapt e.g. the XML namespace declaration, types to + be extended to the equivalents of the module you're actually + using. + +
+ +
+ Core concepts + + The central interface in Spring Data repository abstraction is + Repository (probably not that much of a + surprise). It is typeable to the domain class to manage as well as the id + type of the domain class. This interface mainly acts as marker interface + to capture the types to deal with and help us when discovering interfaces + that extend this one. Beyond that there's + CrudRepository which provides some + sophisticated functionality around CRUD for the entity being + managed. + + + <interfacename>CrudRepository</interfacename> interface + + + + + + + + + + + + + + + + + public interface CrudRepository<T, ID extends Serializable> + extends Repository<T, ID> { + + T save(T entity); + + T findOne(ID primaryKey); + + Iterable<T> findAll(); + + Long count(); + + void delete(T entity); + + boolean exists(ID primaryKey); + + // … more functionality omitted. + } + + + + Saves the given entity. + + + + Returns the entity identified by the given id. + + + + Returns all entities. + + + + Returns the number of entities. + + + + Deletes the given entity. + + + + Returns whether an entity with the given id exists. + + + + + + Usually we will have persistence technology specific sub-interfaces + to include additional technology specific methods. We will now ship + implementations for a variety of Spring Data modules that implement this + interface. + + On top of the CrudRepository there is + a PagingAndSortingRepository abstraction + that adds additional methods to ease paginated access to entities: + + + PagingAndSortingRepository + + public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { + + Iterable<T> findAll(Sort sort); + + Page<T> findAll(Pageable pageable); + } + + + Accessing the second page of User by a page + size of 20 you could simply do something like this: + + PagingAndSortingRepository<User, Long> repository = // … get access to a bean + Page<User> users = repository.findAll(new PageRequest(1, 20)); +
+ +
+ Query methods + + Next to standard CRUD functionality repositories are usually queries + on the underlying datastore. With Spring Data declaring those queries + becomes a four-step process: + + + + Declare an interface extending + Repository or one of its sub-interfaces + and type it to the domain class it shall handle. + + public interface PersonRepository extends Repository<User, Long> { … } + + + + Declare query methods on the interface. + + List<Person> findByLastname(String lastname); + + + + Setup Spring to create proxy instances for those + interfaces. + + <?xml version="1.0" encoding="UTF-8"?> + <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.springframework.org/schema/data/jpa" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/jpa + http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> + + <repositories base-package="com.acme.repositories" /> + + </beans> + + + Note that we use the JPA namespace here just by example. If + you're using the repository abstraction for any other store you need + to change this to the appropriate namespace declaration of your + store module which should be exchanging jpa in favor of + e.g. mongodb. + + + + + Get the repository instance injected and use it. + + public class SomeClient { + + @Autowired + private PersonRepository repository; + + public void doSomething() { + List<Person> persons = repository.findByLastname("Matthews"); + } + + + + At this stage we barely scratched the surface of what's possible + with the repositories but the general approach should be clear. Let's go + through each of these steps and figure out details and various options + that you have at each stage. + +
+ Defining repository interfaces + + As a very first step you define a domain class specific repository + interface. It's got to extend Repository + and be typed to the domain class and an ID type. If you want to expose + CRUD methods for that domain type, extend + CrudRepository instead of + Repository. + +
+ Fine tuning repository definition + + Usually you will have your repository interface extend + Repository, + CrudRepository or + PagingAndSortingRepository. If you + don't like extending Spring Data interfaces at all you can also + annotate your repository interface with + @RepositoryDefinition. Extending + CrudRepository will expose a complete + set of methods to manipulate your entities. If you would rather be + selective about the methods being exposed, simply copy the ones you + want to expose from CrudRepository into + your domain repository. + + + Selectively exposing CRUD methods + + interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> { + T findOne(ID id); + T save(T entity); + } + + interface UserRepository extends MyBaseRepository<User, Long> { + + User findByEmailAddress(EmailAddress emailAddress); + } + + + In the first step we define a common base interface for all our + domain repositories and expose findOne(…) as + well as save(…).These methods will be routed + into the base repository implementation of the store of your choice + because they are matching the method signatures in + CrudRepository. So our + UserRepository will now be able to save + users, find single ones by id as well as triggering a query to find + Users by their email address. +
+
+ +
+ Defining query methods + +
+ Query lookup strategies + + The next thing we have to discuss is the definition of query + methods. There are two main ways that the repository proxy is able to + come up with the store specific query from the method name. The first + option is to derive the query from the method name directly, the + second is using some kind of additionally created query. What detailed + options are available pretty much depends on the actual store, + however, there's got to be some algorithm that decides what actual + query is created. + + There are three strategies available for the repository + infrastructure to resolve the query. The strategy to be used can be + configured at the namespace through the + query-lookup-strategy attribute. However, It might be the + case that some of the strategies are not supported for specific + datastores. Here are your options: + + + CREATE + + This strategy will try to construct a store specific query + from the query method's name. The general approach is to remove a + given set of well-known prefixes from the method name and parse the + rest of the method. Read more about query construction in . + + + + USE_DECLARED_QUERY + + This strategy tries to find a declared query which will be + used for execution first. The query could be defined by an + annotation somewhere or declared by other means. Please consult the + documentation of the specific store to find out what options are + available for that store. If the repository infrastructure does not + find a declared query for the method at bootstrap time it will + fail. + + + + CREATE_IF_NOT_FOUND (default) + + This strategy is actually a combination of CREATE + and USE_DECLARED_QUERY. It will try to lookup a + declared query first but create a custom method name based query if + no declared query was found. This is the default lookup strategy and + thus will be used if you don't configure anything explicitly. It + allows quick query definition by method names but also custom tuning + of these queries by introducing declared queries as needed. + +
+ +
+ Query creation + + The query builder mechanism built into Spring Data repository + infrastructure is useful to build constraining queries over entities + of the repository. We will strip the prefixes findBy, + find, readBy, read, + getBy as well as get from the method and + start parsing the rest of it. At a very basic level you can define + conditions on entity properties and concatenate them with + AND and OR. + + + Query creation from method names + + public interface PersonRepository extends Repository<User, Long> { + + List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname); + } + + + The actual result of parsing that method will of course depend + on the persistence store we create the query for, however, there are + some general things to notice. The expressions are usually property + traversals combined with operators that can be concatenated. As you + can see in the example you can combine property expressions with And + and Or. Beyond that you also get support for various operators like + Between, LessThan, + GreaterThan, Like for the + property expressions. As the operators supported can vary from + datastore to datastore please consult the according part of the + reference documentation. + +
+ Property expressions + + Property expressions can just refer to a direct property of + the managed entity (as you just saw in the example above). On query + creation time we already make sure that the parsed property is at a + property of the managed domain class. However, you can also define + constraints by traversing nested properties. Assume + Persons have Addresses + with ZipCodes. In that case a method name + of + + List<Person> findByAddressZipCode(ZipCode zipCode); + + will create the property traversal + x.address.zipCode. The resolution algorithm starts with + interpreting the entire part (AddressZipCode) as + property and checks the domain class for a property with that name + (uncapitalized). If it succeeds it just uses that. If not it starts + splitting up the source at the camel case parts from the right side + into a head and a tail and tries to find the according property, + e.g. AddressZip and Code. If + we find a property with that head we take the tail and continue + building the tree down from there. As in our case the first split + does not match we move the split point to the left + (Address, ZipCode). + + Although this should work for most cases, there might be cases + where the algorithm could select the wrong property. Suppose our + Person class has an addressZip + property as well. Then our algorithm would match in the first split + round already and essentially choose the wrong property and finally + fail (as the type of addressZip probably has + no code property). To resolve this ambiguity you can use + _ inside your method name to manually define + traversal points. So our method name would end up like so: + + List<Person> findByAddress_ZipCode(ZipCode zipCode); + +
+
+ +
+ Special parameter handling + + To hand parameters to your query you simply define method + parameters as already seen in the examples above. Besides that we will + recognizes certain specific types to apply pagination and sorting to + your queries dynamically. + + + Using Pageable and Sort in query methods + + Page<User> findByLastname(String lastname, Pageable pageable); + + List<User> findByLastname(String lastname, Sort sort); + + List<User> findByLastname(String lastname, Pageable pageable); + + + The first method allows you to pass a Pageable + instance to the query method to dynamically add paging to your + statically defined query. Sorting options are handed via + the Pageable instance too. If you only + need sorting, simply add a Sort parameter to your method. + As you also can see, simply returning a + List is possible as well. We will then + not retrieve the additional metadata required to build the actual + Page instance but rather simply + restrict the query to lookup only the given range of entities. + + + To find out how many pages you get for a query entirely we + have to trigger an additional count query. This will be derived from + the query you actually trigger by default. + +
+
+ +
+ Creating repository instances + + So now the question is how to create instances and bean + definitions for the repository interfaces defined. + +
+ XML Configuration + + The easiest way to do so is by using the Spring namespace that + is shipped with each Spring Data module that supports the repository + mechanism. Each of those includes a repositories element that allows + you to simply define a base package that Spring will scan for + you. + + <?xml version="1.0" encoding="UTF-8"?> + <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.springframework.org/schema/data/jpa" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/jpa + http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> + + <repositories base-package="com.acme.repositories" /> + + </beans:beans> + + In this case we instruct Spring to scan + com.acme.repositories and all its sub packages for + interfaces extending Repository or one + of its sub-interfaces. For each interface found it will register the + persistence technology specific + FactoryBean to create the according + proxies that handle invocations of the query methods. Each of these + beans will be registered under a bean name that is derived from the + interface name, so an interface of + UserRepository would be registered + under userRepository. The base-package + attribute allows the use of wildcards, so that you can have a pattern + of scanned packages. + + + Using filters + + By default we will pick up every interface extending the + persistence technology specific + Repository sub-interface located + underneath the configured base package and create a bean instance + for it. However, you might want finer grained control over which + interfaces bean instances get created for. To do this we support the + use of <include-filter /> and + <exclude-filter /> elements inside + <repositories />. The semantics are exactly + equivalent to the elements in Spring's context namespace. For + details see Spring reference documentation on these + elements. + + E.g. to exclude certain interfaces from instantiation as + repository, you could use the following configuration: + + + Using exclude-filter element + + <repositories base-package="com.acme.repositories"> + <context:exclude-filter type="regex" expression=".*SomeRepository" /> + </repositories> + + This would exclude all interfaces ending in + SomeRepository from being + instantiated. + + +
+ +
+ JavaConfig + + The repository infrastructure can also be triggered using a + store-specific + @Enable${store}Repositories annotation + on a JavaConfig class. For an introduction into Java based + configuration of the Spring container please have a look at the + reference documentation. + JavaConfig in the Spring reference documentation - + + + A sample configuration to enable Spring Data repositories would + look something like this. + + + Sample annotation based repository configuration + + @Configuration + @EnableJpaRepositories("com.acme.repositories") + class ApplicationConfiguration { + + @Bean + public EntityManagerFactory entityManagerFactory() { + // … + } + } + + + Note that the sample uses the JPA specific annotation which + would have to be exchanged dependingon which store module you actually + use. The same applies to the definition of the + EntityManagerFactory bean. Please + consult the sections covering the store-specific configuration. +
+ +
+ Standalone usage + + You can also use the repository infrastructure outside of a + Spring container usage. You will still need to have some of the Spring + libraries on your classpath but you can generally setup repositories + programmatically as well. The Spring Data modules providing repository + support ship a persistence technology specific + RepositoryFactory that can be used as + follows: + + + Standalone usage of repository factory + + RepositoryFactorySupport factory = … // Instantiate factory here + UserRepository repository = factory.getRepository(UserRepository.class); + +
+
+
+ +
+ Custom implementations + +
+ Adding behaviour to single repositories + + Often it is necessary to provide a custom implementation for a few + repository methods. Spring Data repositories easily allow you to provide + custom repository code and integrate it with generic CRUD abstraction + and query method functionality. To enrich a repository with custom + functionality you have to define an interface and an implementation for + that functionality first and let the repository interface you provided + so far extend that custom interface. + + + Interface for custom repository functionality + + interface UserRepositoryCustom { + + public void someCustomMethod(User user); + } + + + + Implementation of custom repository functionality + + class UserRepositoryImpl implements UserRepositoryCustom { + + public void someCustomMethod(User user) { + // Your custom implementation + } + }Note that the implementation itself does not depend on + Spring Data and can be a regular Spring bean. So you can use standard + dependency injection behaviour to inject references to other beans, + take part in aspects and so on. + + + + Changes to the your basic repository interface + + public interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom { + + // Declare query methods here + }Let your standard repository interface extend the custom + one. This makes CRUD and custom functionality available to + clients. + + + + Configuration + + If you use namespace configuration the repository infrastructure + tries to autodetect custom implementations by looking up classes in + the package we found a repository using the naming conventions + appending the namespace element's attribute + repository-impl-postfix to the classname. This suffix + defaults to Impl. + + + Configuration example + + <repositories base-package="com.acme.repository" /> + + <repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" /> + + + The first configuration example will try to lookup a class + com.acme.repository.UserRepositoryImpl to act + as custom repository implementation, where the second example will try + to lookup + com.acme.repository.UserRepositoryFooBar. + + + + Manual wiring + + The approach above works perfectly well if your custom + implementation uses annotation based configuration and autowiring + entirely as it will be treated as any other Spring bean. If your + custom implementation bean needs some special wiring you simply + declare the bean and name it after the conventions just described. We + will then pick up the custom bean by name rather than creating an + instance. + + + Manual wiring of custom implementations (I) + + <repositories base-package="com.acme.repository" /> + + <beans:bean id="userRepositoryImpl" class="…"> + <!-- further configuration --> + </beans:bean> + + +
+ +
+ Adding custom behaviour to all repositories + + In other cases you might want to add a single method to all of + your repository interfaces. So the approach just shown is not feasible. + The first step to achieve this is adding and intermediate interface to + declare the shared behaviour + + + An interface declaring custom shared behaviour + + + public interface MyRepository<T, ID extends Serializable> + extends JpaRepository<T, ID> { + + void sharedCustomMethod(ID id); + } + + + Now your individual repository interfaces will extend this + intermediate interface instead of the + Repository interface to include the + functionality declared. The second step is to create an implementation + of this interface that extends the persistence technology specific + repository base class which will then act as a custom base class for the + repository proxies. + + + The default behaviour of the Spring <repositories + /> namespace is to provide an implementation for all + interfaces that fall under the base-package. This means + that if left in it's current state, an implementation instance of + MyRepository will be created by Spring. + This is of course not desired as it is just supposed to act as an + intermediary between Repository and the + actual repository interfaces you want to define for each entity. To + exclude an interface extending + Repository from being instantiated as a + repository instance it can either be annotate it with + @NoRepositoryBean or moved out side of + the configured base-package. + + + + Custom repository base class + + + public class MyRepositoryImpl<T, ID extends Serializable> + extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> { + + private EntityManager entityManager; + + // There are two constructors to choose from, either can be used. + public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { + super(domainClass, entityManager); + + // This is the recommended method for accessing inherited class dependencies. + this.entityManager = entityManager; + } + + public void sharedCustomMethod(ID id) { + // implementation goes here + } + } + + + The last step is to create a custom repository factory to replace + the default RepositoryFactoryBean that will in + turn produce a custom RepositoryFactory. The new + repository factory will then provide your + MyRepositoryImpl as the implementation of any + interfaces that extend the Repository + interface, replacing the SimpleJpaRepository + implementation you just extended. + + + Custom repository factory bean + + + public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> + extends JpaRepositoryFactoryBean<R, T, I> { + + protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { + + return new MyRepositoryFactory(entityManager); + } + + private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory { + + private EntityManager entityManager; + + public MyRepositoryFactory(EntityManager entityManager) { + super(entityManager); + + this.entityManager = entityManager; + } + + protected Object getTargetRepository(RepositoryMetadata metadata) { + + return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager); + } + + protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { + + // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory + //to check for QueryDslJpaRepository's which is out of scope. + return MyRepository.class; + } + } + } + + + Finally you can either declare beans of the custom factory + directly or use the factory-class attribute of the Spring + namespace to tell the repository infrastructure to use your custom + factory implementation. + + + Using the custom factory with the namespace + + <repositories base-package="com.acme.repository" + factory-class="com.acme.MyRepositoryFactoryBean" /> + +
+
+ +
+ Extensions + + This chapter documents a set of Spring Data extensions that enable + Spring Data usage in a variety of contexts. Currently most of the + integration is targeted towards Spring MVC. + +
+ Domain class web binding for Spring MVC + + Given you are developing a Spring MVC web applications you + typically have to resolve domain class ids from URLs. By default it's + your task to transform that request parameter or URL part into the + domain class to hand it layers below then or execute business logic on + the entities directly. This should look something like this: + + @Controller + @RequestMapping("/users") + public class UserController { + + private final UserRepository userRepository; + + public UserController(UserRepository userRepository) { + userRepository = userRepository; + } + + @RequestMapping("/{id}") + public String showUserForm(@PathVariable("id") Long id, Model model) { + + // Do null check for id + User user = userRepository.findOne(id); + // Do null check for user + // Populate model + return "user"; + } + } + + First you pretty much have to declare a repository dependency for + each controller to lookup the entity managed by the controller or + repository respectively. Beyond that looking up the entity is + boilerplate as well as it's always a findOne(…) + call. Fortunately Spring provides means to register custom converting + components that allow conversion between a String + value to an arbitrary type. + + + PropertyEditors + + For versions up to Spring 3.0 simple Java + PropertyEditors had to be used. Thus, + we offer a DomainClassPropertyEditorRegistrar, + that will look up all Spring Data repositories registered in the + ApplicationContext and register a + custom PropertyEditor for the managed + domain class + + <bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> + <property name="webBindingInitializer"> + <bean class="….web.bind.support.ConfigurableWebBindingInitializer"> + <property name="propertyEditorRegistrars"> + <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" /> + </property> + </bean> + </property> + </bean> + + If you have configured Spring MVC like this you can turn your + controller into the following that reduces a lot of the clutter and + boilerplate. + + @Controller + @RequestMapping("/users") + public class UserController { + + @RequestMapping("/{id}") + public String showUserForm(@PathVariable("id") User user, Model model) { + + // Do null check for user + // Populate model + return "userForm"; + } + } + + + + ConversionService + + As of Spring 3.0 the + PropertyEditor support is superseeded + by a new conversion infrstructure that leaves all the drawbacks of + PropertyEditors behind and uses a + stateless X to Y conversion approach. We now ship with a + DomainClassConverter that pretty much mimics + the behaviour of + DomainClassPropertyEditorRegistrar. To register + the converter you have to declare + ConversionServiceFactoryBean, register the + converter and tell the Spring MVC namespace to use the configured + conversion service: + + <mvc:annotation-driven conversion-service="conversionService" /> + + <bean id="conversionService" class="….context.support.ConversionServiceFactoryBean"> + <property name="converters"> + <list> + <bean class="org.springframework.data.repository.support.DomainClassConverter"> + <constructor-arg ref="conversionService" /> + </bean> + </list> + </property> + </bean> + +
+ +
+ Web pagination + + @Controller + @RequestMapping("/users") + public class UserController { + + // DI code omitted + + @RequestMapping + public String showUsers(Model model, HttpServletRequest request) { + + int page = Integer.parseInt(request.getParameter("page")); + int pageSize = Integer.parseInt(request.getParameter("pageSize")); + model.addAttribute("users", userService.getUsers(pageable)); + return "users"; + } + } + + As you can see the naive approach requires the method to contain + an HttpServletRequest parameter that has + to be parsed manually. We even omitted an appropriate failure handling + which would make the code even more verbose. The bottom line is that the + controller actually shouldn't have to handle the functionality of + extracting pagination information from the request. So we include a + PageableArgumentResolver that will do the work + for you. + + <bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> + <property name="customArgumentResolvers"> + <list> + <bean class="org.springframework.data.web.PageableArgumentResolver" /> + </list> + </property> + </bean> + + This configuration allows you to simplify controllers down to + something like this: + + @Controller + @RequestMapping("/users") + public class UserController { + + @RequestMapping + public String showUsers(Model model, Pageable pageable) { + + model.addAttribute("users", userDao.readAll(pageable)); + return "users"; + } + } + + The PageableArgumentResolver will + automatically resolve request parameters to build a + PageRequest instance. By default it will expect + the following structure for the request parameters: + + + Request parameters evaluated by + <classname>PageableArgumentResolver</classname> + + + + + + + + + page + + The page you want to retrieve + + + + page.size + + The size of the page you want to retrieve + + + + page.sort + + The property that should be sorted by + + + + page.sort.dir + + The direction that should be used for sorting + + + +
+ + In case you need multiple Pageables + to be resolved from the request (for multiple tables e.g.) you can use + Spring's @Qualifier annotation to + distinguish one from another. The request parameters then have to be + prefixed with ${qualifier}_. So a method signature like + this: + + public String showUsers(Model model, + @Qualifier("foo") Pageable first, + @Qualifier("bar") Pageable second) { … } + + + you'd have to populate foo_page and + bar_page and the according subproperties. + + + Defaulting + + The PageableArgumentResolver will use a + PageRequest with the first page and a page size + of 10 by default and will use that in case it can't resolve a + PageRequest from the request (because of + missing parameters e.g.). You can configure a global default on the + bean declaration directly. In case you might need controller method + specific defaults for the Pageable + simply annotate the method parameter with + @PageableDefaults and specify page and + page size as annotation attributes: + + public String showUsers(Model model, + @PageableDefaults(pageNumber = 0, value = 30) Pageable pageable) { … } + + +
+ +
+ Repository populators + + If you have been working with the JDBC module of Spring you're + probably familiar with the support to populate a DataSource using SQL + scripts. A similar abstraction is available on the repositories level + although we don't use SQL as data definition language as we need to be + store independent of course. Thus the populators support XML (through + Spring's OXM abstraction) and JSON (through Jackson) to define data for + the repositories to be populated with. + + Assume you have a file data.json with the + following content: + + + Data defined in JSON + + [ { "_class" : "com.acme.Person", + "firstname" : "Dave", + "lastname" : "Matthews" }, + { "_class" : "com.acme.Person", + "firstname" : "Carter", + "lastname" : "Beauford" } ] + + + You can easily populate you repositories by using the populator + elements of the repository namespace provided in Spring Data Commons. To + get the just shown data be populated to your + PersonRepository all you need to do is + the following: + + + Declaring a Jackson repository populator + + <?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:repository="http://www.springframework.org/schema/data/repository" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/repository + http://www.springframework.org/schema/data/repository/spring-repository.xsd"> + + <repository:jackson-populator location="classpath:data.json" /> + + </beans> + + + This declaration causes the data.json file being read, + deserialized by a Jackson ObjectMapper. The type + the JSON object will be unmarshalled to will be determined by inspecting + the _class attribute of the JSON document. We will + eventually select the appropriate repository being able to handle the + object just deserialized. + + To rather use XML to define the repositories shall be populated + with you can use the unmarshaller-populator you hand one of the + marshaller options Spring OXM provides you with. + + + Declaring an unmarshalling repository populator (using + JAXB) + + <?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:repository="http://www.springframework.org/schema/data/repository" + xmlns:oxm="http://www.springframework.org/schema/oxm" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/data/repository + http://www.springframework.org/schema/data/repository/spring-repository.xsd + http://www.springframework.org/schema/oxm + http://www.springframework.org/schema/oxm/spring-oxm.xsd"> + + <repository:unmarshaller-populator location="classpath:data.json" unmarshaller-ref="unmarshaller" /> + + <oxm:jaxb2-marshaller contextPath="com.acme" /> + + </beans> + +
+
+
\ No newline at end of file diff --git a/src/main/assembly/distribution.xml b/src/main/assembly/distribution.xml new file mode 100644 index 000000000..e8450e0cd --- /dev/null +++ b/src/main/assembly/distribution.xml @@ -0,0 +1,52 @@ + + + + distribution + + zip + + true + + + + src/main/resources + + license.txt + notice.txt + changelog.txt + + + dos + + + + target/site/reference + docs/reference + + + + target/site/apidocs + docs/javadoc + + + + + target/${dist.finalName}.jar + dist + 0644 + + + target/${dist.finalName}-sources.jar + sources + 0644 + + + \ No newline at end of file diff --git a/src/main/assembly/jar-with-dependencies.descriptor.xml b/src/main/assembly/jar-with-dependencies.descriptor.xml new file mode 100644 index 000000000..b34b174fe --- /dev/null +++ b/src/main/assembly/jar-with-dependencies.descriptor.xml @@ -0,0 +1,18 @@ + + all + + jar + + false + + + / + true + true + runtime + true + + + \ No newline at end of file diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 18aca5bcb..efbc1d75f 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,5 +1,4 @@ -Spring Data Solr 1.0 -Copyright (c) [2012] SpringSource, a division of VMware, Inc. +Spring Data Elasticsearch This product is licensed to you under the Apache License, Version 2.0 (the "License"). You may not use this product except in compliance with the License. diff --git a/src/test/java/org/springframework/data/elasticsearch/core/query/CriteriaQueryTest.java b/src/test/java/org/springframework/data/elasticsearch/core/query/CriteriaQueryTest.java index 18562e806..bfe480907 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/query/CriteriaQueryTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/query/CriteriaQueryTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:elasticsearch-template-test.xml") -@Ignore public class CriteriaQueryTest { @Resource @@ -476,13 +475,13 @@ public class CriteriaQueryTest { elasticsearchTemplate.refresh(SampleEntity.class,true); CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("rate").between(350,null)); //when - SampleEntity sampleEntity = elasticsearchTemplate.queryForObject(criteriaQuery, SampleEntity.class); + Page page = elasticsearchTemplate.queryForPage(criteriaQuery, SampleEntity.class); //then - assertThat(sampleEntity,is(notNullValue())); + assertThat(page,is(notNullValue())); + assertThat(page.getTotalElements(),is(greaterThanOrEqualTo(1L))); } @Test - @Ignore public void testBetweenWithoutLowerBound() { //given List indexQueries = new ArrayList(); @@ -513,13 +512,13 @@ public class CriteriaQueryTest { elasticsearchTemplate.refresh(SampleEntity.class,true); CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("rate").between(null,550)); //when - SampleEntity sampleEntity = elasticsearchTemplate.queryForObject(criteriaQuery, SampleEntity.class); + Page page = elasticsearchTemplate.queryForPage(criteriaQuery, SampleEntity.class); //then - assertThat(sampleEntity,is(notNullValue())); + assertThat(page,is(notNullValue())); + assertThat(page.getTotalElements(),is(greaterThanOrEqualTo(1L))); } @Test - @Ignore public void testLessThanEqauls() { //given List indexQueries = new ArrayList(); @@ -550,13 +549,13 @@ public class CriteriaQueryTest { elasticsearchTemplate.refresh(SampleEntity.class,true); CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("rate").lessThanEqual(750)); //when - SampleEntity sampleEntity = elasticsearchTemplate.queryForObject(criteriaQuery, SampleEntity.class); + Page page = elasticsearchTemplate.queryForPage(criteriaQuery, SampleEntity.class); //then - assertThat(sampleEntity,is(notNullValue())); + assertThat(page,is(notNullValue())); + assertThat(page.getTotalElements(),is(greaterThanOrEqualTo(1L))); } @Test - @Ignore public void testGreaterThanEqauls() { //given List indexQueries = new ArrayList(); @@ -587,9 +586,10 @@ public class CriteriaQueryTest { elasticsearchTemplate.refresh(SampleEntity.class,true); CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("rate").greaterThanEqual(950)); //when - SampleEntity sampleEntity = elasticsearchTemplate.queryForObject(criteriaQuery, SampleEntity.class); + Page page = elasticsearchTemplate.queryForPage(criteriaQuery, SampleEntity.class); //then - assertThat(sampleEntity,is(notNullValue())); + assertThat(page,is(notNullValue())); + assertThat(page.getTotalElements(),is(greaterThanOrEqualTo(1L))); } @Test diff --git a/src/test/java/org/springframework/data/elasticsearch/repositories/CustomMethodRepositoryTest.java b/src/test/java/org/springframework/data/elasticsearch/repositories/CustomMethodRepositoryTest.java index 46073da4a..790228a13 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repositories/CustomMethodRepositoryTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/repositories/CustomMethodRepositoryTest.java @@ -1,20 +1,23 @@ package org.springframework.data.elasticsearch.repositories; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.SampleEntity; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.query.DeleteQuery; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; import static org.apache.commons.lang.RandomStringUtils.randomNumeric; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) @@ -24,6 +27,18 @@ public class CustomMethodRepositoryTest { @Resource private SampleCustomMethodRepository repository; + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; + + @Before + public void before(){ + elasticsearchTemplate.createIndex(SampleEntity.class); + DeleteQuery deleteQuery = new DeleteQuery(); + deleteQuery.setElasticsearchQuery(matchAllQuery()); + elasticsearchTemplate.delete(deleteQuery,SampleEntity.class); + elasticsearchTemplate.refresh(SampleEntity.class, true); + } + @Test public void shouldExecuteCustomMethod(){ //given @@ -40,6 +55,22 @@ public class CustomMethodRepositoryTest { assertThat(page.getTotalElements(), is(greaterThanOrEqualTo(1L))); } + @Test + public void shouldExecuteCustomMethodForNext(){ + //given + String documentId = randomNumeric(5); + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setType("some"); + sampleEntity.setMessage("some message"); + repository.save(sampleEntity); + //when + Page page = repository.findByTypeNot("test", new PageRequest(1, 10)); + //then + assertThat(page, is(notNullValue())); + assertThat(page.getTotalElements(), is(greaterThanOrEqualTo(1L))); + } + @Test public void shouldExecuteCustomMethodWithQuery(){ //given @@ -56,4 +87,31 @@ public class CustomMethodRepositoryTest { assertThat(page.getTotalElements(), is(greaterThanOrEqualTo(1L))); } + + @Test + public void shouldExecuteCustomMethodWithLessThan(){ + //given + String documentId = randomNumeric(5); + SampleEntity sampleEntity = new SampleEntity(); + sampleEntity.setId(documentId); + sampleEntity.setType("test"); + sampleEntity.setRate(10); + sampleEntity.setMessage("some message"); + repository.save(sampleEntity); + + String documentId2 = randomNumeric(5); + SampleEntity sampleEntity2 = new SampleEntity(); + sampleEntity2.setId(documentId2); + sampleEntity2.setType("test"); + sampleEntity2.setRate(20); + sampleEntity2.setMessage("some message"); + repository.save(sampleEntity2); + + //when + Page page = repository.findByRateLessThan(10, new PageRequest(1, 10)); + //then + assertThat(page, is(notNullValue())); + assertThat(page.getTotalElements(), is(equalTo(1L))); + } + } diff --git a/src/test/java/org/springframework/data/elasticsearch/repositories/SampleCustomMethodRepository.java b/src/test/java/org/springframework/data/elasticsearch/repositories/SampleCustomMethodRepository.java index f384e2dfd..ba1ddd085 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repositories/SampleCustomMethodRepository.java +++ b/src/test/java/org/springframework/data/elasticsearch/repositories/SampleCustomMethodRepository.java @@ -11,7 +11,13 @@ public interface SampleCustomMethodRepository extends ElasticsearchRepository findByType(String type, Pageable pageable); + Page findByTypeNot(String type, Pageable pageable); + @Query("{\"bool\" : {\"must\" : {\"field\" : {\"message\" : \"?0\"}}}}") Page findByMessage(String message, Pageable pageable); + Page findByRateLessThan(int rate, Pageable pageable); + + Page findByRateBefore(int rate, Pageable pageable); + }