├── .gitignore ├── .travis.yml ├── LICENSE ├── jpa-projections ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── Customer.java │ │ ├── CustomerDto.java │ │ ├── CustomerProjection.java │ │ ├── CustomerRepository.java │ │ ├── CustomerSummary.java │ │ └── package-info.java │ └── test │ └── java │ └── example │ └── CustomerRepositoryIntegrationTest.java ├── jpa-query-by-example ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── SpecialUser.java │ │ ├── User.java │ │ ├── UserRepository.java │ │ └── package-info.java │ └── test │ └── java │ └── example │ ├── ApplicationConfiguration.java │ ├── UserRepositoryInheritanceIntegrationTests.java │ └── UserRepositoryIntegrationTests.java ├── jpa-query-method-wrapped-parameters ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── Person.java │ │ ├── PersonRepository.java │ │ └── package-info.java │ └── test │ └── java │ └── example │ └── PersonRepositoryIntegrationTests.java ├── mongodb-composed-annotations ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── FindWalter.java │ │ ├── ImprovedVenue.java │ │ ├── MyGeoIndexAnnotation.java │ │ ├── Person.java │ │ ├── PersonRepository.java │ │ └── Venue.java │ └── test │ └── java │ └── example │ ├── ApplicationConfiguration.java │ ├── ComposedAnnotationIntegrationTest.java │ └── RepositoryIntegrationTest.java ├── mongodb-query-by-example ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── Contact.java │ │ ├── ContactRepository.java │ │ ├── Person.java │ │ ├── Relative.java │ │ ├── RelativeRepository.java │ │ ├── UserRepository.java │ │ └── package-info.java │ └── test │ └── java │ └── example │ ├── ApplicationConfiguration.java │ ├── ContactRepositoryIntegrationTests.java │ ├── MongoOperationsIntegrationTests.java │ └── UserRepositoryIntegrationTests.java ├── mongodb-reactive ├── pom.xml └── src │ ├── main │ └── java │ │ └── example │ │ ├── Person.java │ │ ├── ReactivePersonRepository.java │ │ ├── ReactorPersonRepository.java │ │ └── RxJavaPersonRepository.java │ └── test │ ├── java │ └── example │ │ ├── ApplicationConfiguration.java │ │ ├── ReactiveMongoOperationsIntegrationTests.java │ │ ├── ReactivePersonRepositoryIntegrationTests.java │ │ ├── ReactorPersonRepositoryIntegrationTests.java │ │ └── RxJavaPersonRepositoryIntegrationTests.java │ └── resources │ └── application.properties ├── pom.xml ├── readme.adoc ├── redis-cluster ├── .gitignore ├── Makefile ├── pom.xml ├── readme.md └── src │ ├── main │ └── java │ │ └── example │ │ └── AppConfig.java │ └── test │ ├── java │ └── example │ │ └── BasicUsageTests.java │ └── resources │ └── application.properties └── redis-repositories ├── .gitignore ├── pom.xml └── src ├── main └── java │ └── example │ ├── City.java │ ├── Person.java │ └── PersonRepository.java └── test └── java └── example ├── EmbeddedRedisRule.java ├── RedisRepositoryTests.java └── RedisTestConfiguration.java /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | .springBeans 4 | .settings/ 5 | target/ 6 | 7 | #IntelliJ Stuff 8 | .idea 9 | *.iml 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | cache: 7 | directories: 8 | - $HOME/.m2 9 | - $HOME/.embedmongo 10 | 11 | sudo: false 12 | 13 | install: make -C redis-cluster start 14 | 15 | script: "mvn clean dependency:list test -Dsort" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /jpa-projections/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-jpa-projections 12 | What's new in Spring Data? - JPA Projections 13 | 14 | 15 | Hopper-SR2 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | 26 | org.hsqldb 27 | hsqldb 28 | runtime 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/Customer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.Id; 21 | 22 | import lombok.Data; 23 | import lombok.RequiredArgsConstructor; 24 | 25 | /** 26 | * @author Oliver Gierke 27 | */ 28 | @Data 29 | @Entity 30 | @RequiredArgsConstructor 31 | public class Customer { 32 | 33 | private @GeneratedValue @Id Long id; 34 | private final String firstname, lastname; 35 | 36 | protected Customer() { 37 | this.firstname = null; 38 | this.lastname = null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/CustomerDto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.Data; 19 | import lombok.RequiredArgsConstructor; 20 | 21 | /** 22 | * @author Oliver Gierke 23 | */ 24 | @Data 25 | @RequiredArgsConstructor 26 | public class CustomerDto { 27 | 28 | private final String firstname; 29 | } 30 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/CustomerProjection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | /** 19 | * 20 | * @author Oliver Gierke 21 | */ 22 | public interface CustomerProjection { 23 | 24 | String getFirstname(); 25 | } 26 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import java.util.Collection; 19 | 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.data.jpa.repository.Query; 22 | import org.springframework.data.repository.CrudRepository; 23 | 24 | /** 25 | * @author Oliver Gierke 26 | */ 27 | public interface CustomerRepository extends CrudRepository { 28 | 29 | /** 30 | * Uses a projection interface to indicate the fields to be returned. As the projection doesn't use any dynamic 31 | * fields, the query execution will be restricted to only the fields needed by the projection. 32 | * 33 | * @return 34 | */ 35 | Collection findAllProjectedBy(); 36 | 37 | /** 38 | * When a projection is used that contains dynamic properties (i.e. SpEL expressions in an {@link Value} annotation), 39 | * the normal target entity will be loaded but dynamically projected so that the target can be referred to in the 40 | * expression. 41 | * 42 | * @return 43 | */ 44 | Collection findAllSummarizedBy(); 45 | 46 | /** 47 | * Projection interfaces can be used with manually declared queries, too. Make sure you alias the projects matching 48 | * the projection fields. 49 | * 50 | * @return 51 | */ 52 | @Query("select c.firstname as firstname, c.lastname as lastname from Customer c") 53 | Collection findsByProjectedColumns(); 54 | 55 | /** 56 | * Uses a concrete DTO type to indicate the fields to be returned. This gets translated into a constructor expression 57 | * in the query. 58 | * 59 | * @return 60 | */ 61 | Collection findAllDtoedBy(); 62 | 63 | /** 64 | * Passes in the projection type dynamically (either interface or DTO). 65 | * 66 | * @param firstname 67 | * @param projection 68 | * @return 69 | */ 70 | Collection findByFirstname(String firstname, Class projection); 71 | 72 | /** 73 | * Projection for a single entity. 74 | * 75 | * @param id 76 | * @return 77 | */ 78 | CustomerProjection findProjectedById(Long id); 79 | 80 | /** 81 | * Dynamic projection for a single entity. 82 | * 83 | * @param id 84 | * @param projection 85 | * @return 86 | */ 87 | T findProjectedById(Long id, Class projection); 88 | } 89 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/CustomerSummary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | 20 | /** 21 | * @author Oliver Gierke 22 | */ 23 | public interface CustomerSummary { 24 | 25 | @Value("#{target.firstname + ' ' + target.lastname}") 26 | String getFullName(); 27 | } 28 | -------------------------------------------------------------------------------- /jpa-projections/src/main/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample showing Projection on Query methods related features of Spring Data JPA. 3 | * 4 | * @author Mark Paluch 5 | */ 6 | package example; 7 | -------------------------------------------------------------------------------- /jpa-projections/src/test/java/example/CustomerRepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.Collection; 22 | import java.util.Map; 23 | 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.boot.test.context.SpringBootTest; 30 | import org.springframework.data.projection.TargetAware; 31 | import org.springframework.test.context.junit4.SpringRunner; 32 | import org.springframework.transaction.annotation.Transactional; 33 | 34 | /** 35 | * Integration tests for {@link CustomerRepository} to show projection capabilities. 36 | * 37 | * @author Oliver Gierke 38 | * @author Mark Paluch 39 | */ 40 | @RunWith(SpringRunner.class) 41 | @Transactional 42 | @SpringBootTest 43 | @SpringBootApplication 44 | public class CustomerRepositoryIntegrationTest { 45 | 46 | @Autowired CustomerRepository customers; 47 | 48 | Customer dave, carter; 49 | 50 | @Before 51 | public void setUp() { 52 | 53 | this.dave = customers.save(new Customer("Dave", "Matthews")); 54 | this.carter = customers.save(new Customer("Carter", "Beauford")); 55 | } 56 | 57 | @Test 58 | public void projectsEntityIntoInterface() { 59 | 60 | Collection result = customers.findAllProjectedBy(); 61 | 62 | assertThat(result, hasSize(2)); 63 | assertThat(result.iterator().next().getFirstname(), is("Dave")); 64 | } 65 | 66 | @Test 67 | public void projectsEntityIntoOpenProjectionInterface() { 68 | 69 | Collection result = customers.findAllSummarizedBy(); 70 | 71 | assertThat(result, hasSize(2)); 72 | assertThat(result.iterator().next().getFullName(), is("Dave Matthews")); 73 | } 74 | 75 | @Test 76 | public void projectsMapIntoInterface() { 77 | 78 | Collection result = customers.findsByProjectedColumns(); 79 | 80 | assertThat(result, hasSize(2)); 81 | assertThat(result.iterator().next().getFirstname(), is("Dave")); 82 | } 83 | 84 | @Test 85 | public void projectsToDto() { 86 | 87 | Collection result = customers.findAllDtoedBy(); 88 | 89 | assertThat(result, hasSize(2)); 90 | assertThat(result.iterator().next().getFirstname(), is("Dave")); 91 | } 92 | 93 | @Test 94 | public void projectsDynamically() { 95 | 96 | Collection result = customers.findByFirstname("Dave", CustomerProjection.class); 97 | 98 | assertThat(result, hasSize(1)); 99 | assertThat(result.iterator().next().getFirstname(), is("Dave")); 100 | } 101 | 102 | @Test 103 | public void projectsIndividualDynamically() { 104 | 105 | CustomerSummary result = customers.findProjectedById(dave.getId(), CustomerSummary.class); 106 | 107 | assertThat(result.getFullName(), is("Dave Matthews")); 108 | 109 | // Proxy backed by original instance as the projection uses dynamic elements 110 | assertThat(((TargetAware) result).getTarget(), is(instanceOf(Customer.class))); 111 | } 112 | 113 | @Test 114 | public void projectIndividualInstance() { 115 | 116 | CustomerProjection projectedDave = customers.findProjectedById(dave.getId()); 117 | 118 | assertThat(projectedDave.getFirstname(), is("Dave")); 119 | assertThat(((TargetAware) projectedDave).getTarget(), is(instanceOf(Map.class))); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /jpa-query-by-example/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data JPA - Query-by-Example (QBE) example 2 | 3 | This project contains samples of Query-by-Example of Spring Data JPA. 4 | 5 | ## Support for Query-by-Example 6 | 7 | Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require to write queries containing field names. In fact, Query by Example does not require to write queries using JPA-QL at all. 8 | 9 | An `Example` takes a data object (usually the entity object or a subtype of it) and a specification how to match properties. You can use Query by Example with JPA Repositories. 10 | 11 | ```java 12 | public interface PersonRepository extends CrudRepository, QueryByExampleExecutor { 13 | } 14 | ``` 15 | 16 | ```java 17 | Example example = Example.of(new Person("Jon", "Snow")); 18 | repo.findAll(example); 19 | 20 | 21 | ExampleMatcher matcher = ExampleMatcher.matching(). 22 | .withMatcher("firstname", endsWith()) 23 | .withMatcher("lastname", startsWith().ignoreCase()); 24 | 25 | Example example = Example.of(new Person("Jon", "Snow"), matcher); 26 | repo.count(example); 27 | ``` 28 | 29 | This example contains a test class to illustrate Query-by-Example with a Repository in `UserRepositoryIntegrationTests`. 30 | -------------------------------------------------------------------------------- /jpa-query-by-example/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-jpa-query-by-example 12 | What's new in Spring Data? - JPA Query-by-Example (QBE) 13 | 14 | 15 | Hopper-SR2 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | 26 | org.hsqldb 27 | hsqldb 28 | runtime 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/main/java/example/SpecialUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.Data; 19 | import lombok.NoArgsConstructor; 20 | 21 | import javax.persistence.Entity; 22 | 23 | /** 24 | * Sample class that extends {@link User}. 25 | * 26 | * @author Mark Paluch 27 | * @author Oliver Gierke 28 | */ 29 | @Entity 30 | @Data 31 | @NoArgsConstructor 32 | public class SpecialUser extends User { 33 | 34 | public SpecialUser(String firstname, String lastname, Integer age) { 35 | super(firstname, lastname, age); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/main/java/example/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.Data; 19 | import lombok.NoArgsConstructor; 20 | import lombok.RequiredArgsConstructor; 21 | 22 | import javax.persistence.Entity; 23 | import javax.persistence.GeneratedValue; 24 | import javax.persistence.Id; 25 | 26 | /** 27 | * Sample user class. 28 | * 29 | * @author Mark Paluch 30 | * @author Oliver Gierke 31 | */ 32 | @Entity 33 | @Data 34 | @NoArgsConstructor(force = true) 35 | @RequiredArgsConstructor 36 | public class User { 37 | 38 | private @Id @GeneratedValue Long id; 39 | private final String firstname, lastname; 40 | private final Integer age; 41 | } 42 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/main/java/example/UserRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.repository.CrudRepository; 19 | import org.springframework.data.repository.query.QueryByExampleExecutor; 20 | 21 | /** 22 | * Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and 23 | * allows execution of methods accepting {@link org.springframework.data.domain.Example}. 24 | * 25 | * @author Mark Paluch 26 | */ 27 | public interface UserRepository extends CrudRepository, QueryByExampleExecutor {} 28 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/main/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample showing Query-by-Example related features of Spring Data JPA. 3 | * 4 | * @author Mark Paluch 5 | */ 6 | package example.springdata.jpa.querybyexample; 7 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/test/java/example/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.boot.autoconfigure.SpringBootApplication; 19 | 20 | /** 21 | * @author Mark Paluch 22 | * @author Oliver Gierke 23 | */ 24 | @SpringBootApplication 25 | public class ApplicationConfiguration {} 26 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/test/java/example/UserRepositoryInheritanceIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.test.context.SpringBootTest; 26 | import org.springframework.data.domain.Example; 27 | import org.springframework.test.context.junit4.SpringRunner; 28 | import org.springframework.transaction.annotation.Transactional; 29 | 30 | /** 31 | * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories and entities 32 | * using inheritance. 33 | * 34 | * @author Mark Paluch 35 | * @author Oliver Gierke 36 | */ 37 | @RunWith(SpringRunner.class) 38 | @Transactional 39 | @SpringBootTest 40 | public class UserRepositoryInheritanceIntegrationTests { 41 | 42 | @Autowired UserRepository repository; 43 | 44 | User skyler, walter, flynn; 45 | 46 | @Before 47 | public void setUp() { 48 | 49 | repository.deleteAll(); 50 | 51 | this.skyler = repository.save(new User("Skyler", "White", 45)); 52 | this.walter = repository.save(new SpecialUser("Walter", "White", 50)); 53 | this.flynn = repository.save(new SpecialUser("Walter Jr. (Flynn)", "White", 17)); 54 | } 55 | 56 | @Test 57 | public void countByExample() { 58 | assertThat(repository.count(Example.of(new User(null, "White", null))), is(3L)); 59 | } 60 | 61 | @Test 62 | public void countSubtypesByExample() { 63 | assertThat(repository.count(Example.of(new SpecialUser(null, "White", null))), is(2L)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /jpa-query-by-example/src/test/java/example/UserRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | import static org.springframework.data.domain.ExampleMatcher.*; 21 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*; 22 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith; 23 | 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.data.domain.Example; 30 | import org.springframework.data.domain.ExampleMatcher.StringMatcher; 31 | import org.springframework.test.context.junit4.SpringRunner; 32 | import org.springframework.transaction.annotation.Transactional; 33 | 34 | /** 35 | * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories. 36 | * 37 | * @author Mark Paluch 38 | * @author Oliver Gierke 39 | */ 40 | @SuppressWarnings("unused") 41 | @RunWith(SpringRunner.class) 42 | @Transactional 43 | @SpringBootTest 44 | public class UserRepositoryIntegrationTests { 45 | 46 | @Autowired UserRepository repository; 47 | 48 | User skyler, walter, flynn, marie, hank; 49 | 50 | @Before 51 | public void setUp() { 52 | 53 | repository.deleteAll(); 54 | 55 | this.skyler = repository.save(new User("Skyler", "White", 45)); 56 | this.walter = repository.save(new User("Walter", "White", 50)); 57 | this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17)); 58 | this.marie = repository.save(new User("Marie", "Schrader", 38)); 59 | this.hank = repository.save(new User("Hank", "Schrader", 43)); 60 | } 61 | 62 | @Test 63 | public void countBySimpleExample() { 64 | 65 | Example example = Example.of(new User(null, "White", null)); 66 | 67 | assertThat(repository.count(example), is(3L)); 68 | } 69 | 70 | @Test 71 | public void ignorePropertiesAndMatchByAge() { 72 | 73 | Example example = Example.of(flynn, matching().// 74 | withIgnorePaths("firstname", "lastname")); 75 | 76 | assertThat(repository.findOne(example), is(flynn)); 77 | } 78 | 79 | @Test 80 | public void substringMatching() { 81 | 82 | Example example = Example.of(new User("er", null, null), matching().// 83 | withStringMatcher(StringMatcher.ENDING)); 84 | 85 | assertThat(repository.findAll(example), hasItems(skyler, walter)); 86 | } 87 | 88 | @Test 89 | public void matchStartingStringsIgnoreCase() { 90 | 91 | Example example = Example.of(new User("Walter", "WHITE", null), 92 | matching().// 93 | withIgnorePaths("age").// 94 | withMatcher("firstname", startsWith()).// 95 | withMatcher("lastname", ignoreCase())); 96 | 97 | assertThat(repository.findAll(example), hasItems(flynn, walter)); 98 | } 99 | 100 | @Test 101 | public void configuringMatchersUsingLambdas() { 102 | 103 | Example example = Example.of(new User("Walter", "WHITE", null), 104 | matching().// 105 | withIgnorePaths("age").// 106 | withMatcher("firstname", matcher -> matcher.startsWith()).// 107 | withMatcher("lastname", matcher -> matcher.ignoreCase())); 108 | 109 | assertThat(repository.findAll(example), hasItems(flynn, walter)); 110 | } 111 | 112 | @Test 113 | public void valueTransformer() { 114 | 115 | Example example = Example.of(new User(null, "White", 99), matching(). // 116 | withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)))); 117 | 118 | assertThat(repository.findAll(example), hasItems(walter)); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /jpa-query-method-wrapped-parameters/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-jpa-query-method-wrapped-parameters 12 | What's new in Spring Data? - JPA Wrapped query method parameters 13 | 14 | 15 | Hopper-SR2 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | 26 | org.hsqldb 27 | hsqldb 28 | runtime 29 | 30 | 31 | 32 | com.google.guava 33 | guava 34 | 19.0 35 | 36 | 37 | 38 | org.scala-lang 39 | scala-library 40 | 2.11.7 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /jpa-query-method-wrapped-parameters/src/main/java/example/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.Id; 21 | 22 | import lombok.Data; 23 | import lombok.NoArgsConstructor; 24 | import lombok.RequiredArgsConstructor; 25 | 26 | /** 27 | * Sample user class. 28 | * 29 | * @author Mark Paluch 30 | */ 31 | @Entity 32 | @Data 33 | @NoArgsConstructor(force = true) 34 | @RequiredArgsConstructor 35 | public class Person { 36 | 37 | private @Id @GeneratedValue Long id; 38 | private final String firstname, lastname; 39 | private final String gender; 40 | } 41 | -------------------------------------------------------------------------------- /jpa-query-method-wrapped-parameters/src/main/java/example/PersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import java.util.List; 19 | import java.util.Optional; 20 | 21 | import org.springframework.data.repository.CrudRepository; 22 | 23 | import scala.Option; 24 | 25 | /** 26 | * Simple repository interface for {@link Person} instances. 27 | * 28 | * @author Mark Paluch 29 | */ 30 | public interface PersonRepository extends CrudRepository { 31 | 32 | /** 33 | * @param gender a plain {@link String} 34 | * @return 35 | */ 36 | List findByGender(String gender); 37 | 38 | /** 39 | * @param gender a Java 8 {@link Optional} 40 | * @return 41 | */ 42 | List findByGender(Optional gender); 43 | 44 | /** 45 | * @param gender a Guava {@link com.google.common.base.Optional} 46 | * @return 47 | */ 48 | List findByGender(com.google.common.base.Optional gender); 49 | 50 | /** 51 | * @param gender a Scala {@link Option} 52 | * @return 53 | */ 54 | List findByGender(Option gender); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /jpa-query-method-wrapped-parameters/src/main/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample showing Query-by-Example related features of Spring Data JPA. 3 | * 4 | * @author Mark Paluch 5 | */ 6 | package example.springdata.jpa.querybyexample; 7 | -------------------------------------------------------------------------------- /jpa-query-method-wrapped-parameters/src/test/java/example/PersonRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.Optional; 22 | 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.autoconfigure.SpringBootApplication; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.test.context.junit4.SpringRunner; 30 | import org.springframework.transaction.annotation.Transactional; 31 | 32 | import scala.Option; 33 | 34 | /** 35 | * Integration test showing the usage of JPA wrapped query method parameters support through Spring Data repositories. 36 | * 37 | * @author Mark Paluch 38 | */ 39 | @SuppressWarnings("unused") 40 | @RunWith(SpringRunner.class) 41 | @Transactional 42 | @SpringBootTest 43 | @SpringBootApplication 44 | public class PersonRepositoryIntegrationTests { 45 | 46 | @Autowired PersonRepository repository; 47 | 48 | Person gomez, mortica, thing, itt; 49 | 50 | @Before 51 | public void setUp() { 52 | 53 | repository.deleteAll(); 54 | 55 | this.gomez = repository.save(new Person("Gomez", "Addams", "male")); 56 | this.mortica = repository.save(new Person("Mortica", "Addams", "female")); 57 | this.thing = repository.save(new Person("Thing T.", "Thing", null)); 58 | this.itt = repository.save(new Person("Cousin", "Itt", null)); 59 | } 60 | 61 | @Test 62 | public void findByGender() { 63 | 64 | assertThat(repository.findByGender("male"), contains(gomez)); 65 | 66 | assertThat(repository.findByGender((String) null), hasItems(thing, itt)); 67 | assertThat(repository.findByGender((String) null), not(hasItems(gomez, mortica))); 68 | } 69 | 70 | @Test 71 | public void findByGenderWrappedIn8Optional() { 72 | 73 | assertThat(repository.findByGender(Optional.of("male")), contains(gomez)); 74 | 75 | assertThat(repository.findByGender(Optional.empty()), hasItems(thing, itt)); 76 | assertThat(repository.findByGender(Optional.empty()), not(hasItems(gomez, mortica))); 77 | } 78 | 79 | @Test 80 | public void findByGenderWrappedInGuavaOptional() { 81 | 82 | assertThat(repository.findByGender(com.google.common.base.Optional.of("male")), contains(gomez)); 83 | 84 | assertThat(repository.findByGender(com.google.common.base.Optional.absent()), hasItems(thing, itt)); 85 | assertThat(repository.findByGender(com.google.common.base.Optional.absent()), not(hasItems(gomez, mortica))); 86 | } 87 | 88 | @Test 89 | public void findByGenderWrappedInScalaOption() { 90 | 91 | assertThat(repository.findByGender(Option.apply("male")), contains(gomez)); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-mongodb-composed-annotations 12 | What's new in Spring Data? - Composed Annotations 13 | 14 | 15 | Hopper-SR2 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-mongodb 23 | 24 | 25 | 26 | de.flapdoodle.embed 27 | de.flapdoodle.embed.mongo 28 | runtime 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/FindWalter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | import org.springframework.core.annotation.AliasFor; 24 | import org.springframework.data.mongodb.repository.Query; 25 | 26 | /** 27 | * @author Mark Paluch 28 | */ 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Target({ ElementType.METHOD }) 31 | @Query 32 | @interface FindWalter { 33 | 34 | @AliasFor(annotation = Query.class, attribute = "value") 35 | String query() default "{ 'firstname' : 'Walter' }"; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/ImprovedVenue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.annotation.Id; 19 | import org.springframework.data.geo.Point; 20 | import org.springframework.data.mongodb.core.geo.GeoJsonPoint; 21 | import org.springframework.data.mongodb.core.mapping.Document; 22 | 23 | import lombok.Data; 24 | import lombok.NoArgsConstructor; 25 | 26 | /** 27 | * @author Mark Paluch 28 | */ 29 | @Data 30 | @NoArgsConstructor 31 | @Document 32 | public class ImprovedVenue { 33 | 34 | @Id String id; 35 | String address; 36 | 37 | @MyGeoIndexAnnotation("index_name") Point point; 38 | 39 | public ImprovedVenue(String address, Point point) { 40 | this.address = address; 41 | this.point = point; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/MyGeoIndexAnnotation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | import org.springframework.core.annotation.AliasFor; 24 | import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; 25 | import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; 26 | 27 | /** 28 | * @author Mark Paluch 29 | */ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target({ ElementType.FIELD }) 32 | @GeoSpatialIndexed 33 | @interface MyGeoIndexAnnotation { 34 | 35 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "name") 36 | String value(); 37 | 38 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "bits") 39 | int bits() default 2; 40 | 41 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "max") 42 | int max() default 50; 43 | 44 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "min") 45 | int min() default -120; 46 | 47 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "type") 48 | GeoSpatialIndexType type() default GeoSpatialIndexType.GEO_2D; 49 | 50 | @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "useGeneratedName") 51 | boolean dont_change_me() default false; 52 | 53 | String comment() default "What light?"; 54 | } 55 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.annotation.Id; 19 | import org.springframework.data.mongodb.core.mapping.Document; 20 | 21 | import lombok.Data; 22 | import lombok.NoArgsConstructor; 23 | 24 | /** 25 | * @author Mark Paluch 26 | */ 27 | @Document 28 | @Data 29 | @NoArgsConstructor 30 | public class Person { 31 | 32 | @Id String id; 33 | String firstname; 34 | String lastname; 35 | 36 | public Person(String firstname, String lastname){ 37 | this.firstname = firstname; 38 | this.lastname = lastname; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/PersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.data.mongodb.repository.Query; 21 | import org.springframework.data.repository.CrudRepository; 22 | 23 | /** 24 | * @author Mark Paluch 25 | */ 26 | public interface PersonRepository extends CrudRepository { 27 | 28 | @Query("{ 'firstname' : 'Walter' }") 29 | List findWalterUsingQueryAnnotation(); 30 | 31 | @FindWalter 32 | List findWalterUsingComposedAnnotations(); 33 | } 34 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/main/java/example/Venue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.annotation.Id; 19 | import org.springframework.data.geo.Point; 20 | import org.springframework.data.mongodb.core.geo.GeoJsonPoint; 21 | import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; 22 | import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; 23 | import org.springframework.data.mongodb.core.mapping.Document; 24 | 25 | import lombok.Data; 26 | import lombok.NoArgsConstructor; 27 | 28 | /** 29 | * @author Mark Paluch 30 | */ 31 | @Data 32 | @NoArgsConstructor 33 | @Document 34 | public class Venue { 35 | 36 | @Id String id; 37 | String address; 38 | 39 | @GeoSpatialIndexed(bits = 2, max = 50, min = -120, type = GeoSpatialIndexType.GEO_2D, useGeneratedName = false, 40 | name = "myGeoIndex") Point point; 41 | 42 | public Venue(String address, Point point) { 43 | this.address = address; 44 | this.point = point; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/test/java/example/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.boot.autoconfigure.SpringBootApplication; 19 | 20 | /** 21 | * @author Mark Paluch 22 | */ 23 | @SpringBootApplication 24 | public class ApplicationConfiguration {} 25 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/test/java/example/ComposedAnnotationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.test.context.SpringBootTest; 26 | import org.springframework.data.geo.GeoResult; 27 | import org.springframework.data.geo.GeoResults; 28 | import org.springframework.data.geo.Metrics; 29 | import org.springframework.data.geo.Point; 30 | import org.springframework.data.mongodb.core.MongoOperations; 31 | import org.springframework.data.mongodb.core.index.GeospatialIndex; 32 | import org.springframework.data.mongodb.core.query.NearQuery; 33 | import org.springframework.data.mongodb.core.query.Query; 34 | import org.springframework.test.context.junit4.SpringRunner; 35 | 36 | /** 37 | * Integration test showing the usage of MongoDB Composed Annotations support. 38 | * 39 | * @author Mark Paluch 40 | */ 41 | @SuppressWarnings("unused") 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest 44 | public class ComposedAnnotationIntegrationTest { 45 | 46 | @Autowired MongoOperations operations; 47 | 48 | Venue theWhiteResidence, jessesHouse; 49 | ImprovedVenue carWash, pollosHermanos, saulsOffice; 50 | 51 | @Before 52 | public void setUp() { 53 | 54 | operations.remove(new Query(), Venue.class); 55 | operations.remove(new Query(), ImprovedVenue.class); 56 | 57 | this.theWhiteResidence = new Venue("308 Negra Arroyo Lane, Albuquerque, New Mexico, 87104", 58 | new Point(-106.5387498, 35.1261101)); 59 | 60 | this.jessesHouse = new Venue("9809 Margo Street, Albuquerque, New Mexico 87104", 61 | new Point(-106.6677747, 35.087538)); 62 | 63 | this.carWash = new ImprovedVenue("9516 Snow Heights Circle NE, Albuqerque, New Mexico 87112", 64 | new Point(-106.5369764, 35.1084638)); 65 | 66 | this.pollosHermanos = new ImprovedVenue("12000 – 12100 Coors Rd SW, Albuquerque NM, 87045", 67 | new Point(-106.688545, 35.0146382)); 68 | 69 | this.saulsOffice = new ImprovedVenue("9800 Montgomery Blvd NE, Albuquerque, New Mexico, 87111", 70 | new Point(-106.5346129, 35.1293502)); 71 | 72 | operations.save(this.theWhiteResidence); 73 | operations.save(this.jessesHouse); 74 | operations.save(this.carWash); 75 | operations.save(this.pollosHermanos); 76 | operations.save(this.saulsOffice); 77 | } 78 | 79 | /** 80 | * Issue a {@code geoNear} query using a 2d legacy index specified by the built-in {@link GeospatialIndex} annotation. 81 | */ 82 | @Test 83 | public void findsVenuesWithRegularAnnotations() { 84 | 85 | GeoResults geoResults = operations 86 | .geoNear(NearQuery.near(jessesHouse.getPoint()).maxDistance(10, Metrics.MILES), Venue.class); 87 | 88 | assertThat(geoResults.getContent(), hasSize(2)); 89 | 90 | GeoResult geoResult = geoResults.getContent().get(1); 91 | 92 | assertThat(geoResult.getContent(), is(equalTo(theWhiteResidence))); 93 | assertThat(geoResult.getDistance().getValue(), is(closeTo(7.7, 0.1))); 94 | } 95 | 96 | /** 97 | * Issue a {@code geoNear} query using a 2d legacy index specified by a custom, composed {@link MyGeoIndexAnnotation} 98 | * annotation. 99 | */ 100 | @Test 101 | public void findsVenuesWithComposedAnnotations() { 102 | 103 | GeoResults geoResults = operations 104 | .geoNear(NearQuery.near(theWhiteResidence.getPoint()).maxDistance(2, Metrics.MILES), ImprovedVenue.class); 105 | 106 | assertThat(geoResults.getContent(), hasSize(2)); 107 | 108 | GeoResult geoResult = geoResults.getContent().get(1); 109 | 110 | assertThat(geoResult.getContent(), is(equalTo(carWash))); 111 | assertThat(geoResult.getDistance().getValue(), is(closeTo(1.2, 0.1))); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /mongodb-composed-annotations/src/test/java/example/RepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.test.context.SpringBootTest; 28 | import org.springframework.data.mongodb.repository.Query; 29 | import org.springframework.test.context.junit4.SpringRunner; 30 | 31 | /** 32 | * Integration test showing the usage of MongoDB Composed Annotations support through a MongoDB repository. 33 | * 34 | * @author Mark Paluch 35 | */ 36 | @SuppressWarnings("unused") 37 | @RunWith(SpringRunner.class) 38 | @SpringBootTest 39 | public class RepositoryIntegrationTest { 40 | 41 | @Autowired PersonRepository repository; 42 | 43 | Person skyler, walter, flynn; 44 | 45 | @Before 46 | public void setUp() { 47 | 48 | repository.deleteAll(); 49 | 50 | this.skyler = repository.save(new Person("Skyler", "White")); 51 | this.walter = repository.save(new Person("Walter", "White")); 52 | this.flynn = repository.save(new Person("Walter Jr. (Flynn)", "White")); 53 | } 54 | 55 | /** 56 | * Execute a query method annotated with the built-in {@link Query} annotation. 57 | */ 58 | @Test 59 | public void findWalterUsingQueryAnnotation() { 60 | 61 | List result = repository.findWalterUsingQueryAnnotation(); 62 | 63 | assertThat(result, contains(walter)); 64 | } 65 | 66 | /** 67 | * Execute a query method annotated with a custom {@link FindWalter} annotation. 68 | */ 69 | @Test 70 | public void findWalterUsingComposedAnnotations() { 71 | 72 | List result = repository.findWalterUsingComposedAnnotations(); 73 | 74 | assertThat(result, contains(walter)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /mongodb-query-by-example/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data MongoDB - Query-by-Example (QBE) example 2 | 3 | This project contains samples of Query-by-Example of Spring Data MongoDB. 4 | 5 | ## Support for Query-by-Example 6 | 7 | Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require to write queries containing field names. In fact, Query by Example does not require to write queries using JPA-QL at all. 8 | 9 | An `Example` takes a data object (usually the entity object or a subtype of it) and a specification how to match properties. You can use Query by Example with `MongoOperations` and Repositories. 10 | 11 | ```java 12 | public interface PersonRepository extends CrudRepository, QueryByExampleExecutor { 13 | } 14 | ``` 15 | 16 | ```java 17 | Example example = Example.of(new Person("Jon", "Snow")); 18 | repo.findAll(example); 19 | 20 | 21 | ExampleMatcher matcher = ExampleMatcher.matching(). 22 | .withMatcher("firstname", endsWith()) 23 | .withMatcher("lastname", startsWith().ignoreCase()); 24 | 25 | Example example = Example.of(new Person("Jon", "Snow"), matcher); 26 | repo.count(example); 27 | ``` 28 | 29 | This example contains two test classes to illustrate Query-by-Example with `MongoOperations` in `MongoOperationsIntegrationTests` and the usage with a Repository in `UserRepositoryIntegrationTests`. 30 | 31 | -------------------------------------------------------------------------------- /mongodb-query-by-example/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-mongodb-query-by-example 12 | What's new in Spring Data? - MongoDB Query-by-Example (QBE) 13 | 14 | 15 | Hopper-SR2 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-mongodb 23 | 24 | 25 | 26 | de.flapdoodle.embed 27 | de.flapdoodle.embed.mongo 28 | runtime 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/Contact.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.EqualsAndHashCode; 19 | import lombok.Getter; 20 | import lombok.ToString; 21 | 22 | import org.bson.types.ObjectId; 23 | import org.springframework.data.mongodb.core.mapping.Document; 24 | 25 | /** 26 | * @author Oliver Gierke 27 | */ 28 | @Document(collection = "contacts") 29 | @EqualsAndHashCode 30 | @ToString 31 | public abstract class Contact { 32 | 33 | private @Getter ObjectId id; 34 | } 35 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/ContactRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import example.Contact; 19 | 20 | import org.bson.types.ObjectId; 21 | import org.springframework.data.repository.CrudRepository; 22 | import org.springframework.data.repository.query.QueryByExampleExecutor; 23 | 24 | /** 25 | * Repository interface for {@link Contact} and sub-types. 26 | * 27 | * @author Oliver Gierke 28 | */ 29 | public interface ContactRepository extends CrudRepository, QueryByExampleExecutor {} 30 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.Getter; 19 | import lombok.RequiredArgsConstructor; 20 | 21 | import org.springframework.data.mongodb.core.mapping.Document; 22 | 23 | /** 24 | * Sample user class. 25 | * 26 | * @author Mark Paluch 27 | * @author Oliver Gierke 28 | */ 29 | @Getter 30 | @RequiredArgsConstructor 31 | @Document(collection = "contacts") 32 | public class Person extends Contact { 33 | 34 | private final String firstname, lastname; 35 | private final Integer age; 36 | } 37 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/Relative.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import lombok.Getter; 19 | import lombok.RequiredArgsConstructor; 20 | 21 | import org.springframework.data.mongodb.core.mapping.Document; 22 | 23 | /** 24 | * Sample contact class. 25 | * 26 | * @author Mark Paluch 27 | * @author Oliver Gierke 28 | */ 29 | @Getter 30 | @RequiredArgsConstructor 31 | @Document(collection = "contacts") 32 | public class Relative extends Contact { 33 | 34 | private final String firstname, lastname; 35 | private final Integer age; 36 | } 37 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/RelativeRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.repository.CrudRepository; 19 | import org.springframework.data.repository.query.QueryByExampleExecutor; 20 | 21 | /** 22 | * Simple repository interface for {@link Relative} instances. The interface implements {@link QueryByExampleExecutor} 23 | * and allows execution of methods accepting {@link org.springframework.data.domain.Example}. 24 | * 25 | * @author Mark Paluch 26 | */ 27 | public interface RelativeRepository extends CrudRepository, QueryByExampleExecutor {} 28 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/UserRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.data.repository.CrudRepository; 19 | import org.springframework.data.repository.query.QueryByExampleExecutor; 20 | 21 | /** 22 | * Simple repository interface for {@link Person} instances. The interface implements {@link QueryByExampleExecutor} and 23 | * allows execution of methods accepting {@link org.springframework.data.domain.Example}. 24 | * 25 | * @author Mark Paluch 26 | */ 27 | public interface UserRepository extends CrudRepository, QueryByExampleExecutor {} 28 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/main/java/example/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Sample showing Query-by-Example related features of Spring Data MongoDB. 19 | * 20 | * @author Mark Paluch 21 | */ 22 | package example; 23 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/test/java/example/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.boot.autoconfigure.SpringBootApplication; 19 | 20 | /** 21 | * @author Mark Paluch 22 | */ 23 | @SpringBootApplication 24 | public class ApplicationConfiguration {} 25 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/test/java/example/ContactRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.CoreMatchers.is; 19 | import static org.hamcrest.CoreMatchers.not; 20 | import static org.hamcrest.Matchers.*; 21 | import static org.junit.Assert.*; 22 | import static org.springframework.data.domain.ExampleMatcher.*; 23 | 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.data.domain.Example; 30 | import org.springframework.data.domain.ExampleMatcher.*; 31 | import org.springframework.data.mongodb.core.MongoOperations; 32 | import org.springframework.test.context.junit4.SpringRunner; 33 | 34 | /** 35 | * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories for a case 36 | * where two domain types are stored in one collection. 37 | * 38 | * @author Mark Paluch 39 | * @author Oliver Gierke 40 | * @soundtrack Paul van Dyk - VONYC Sessions Episode 496 with guest Armin van Buuren 41 | */ 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest 44 | public class ContactRepositoryIntegrationTests { 45 | 46 | @Autowired UserRepository userRepository; 47 | @Autowired ContactRepository contactRepository; 48 | @Autowired MongoOperations mongoOperations; 49 | 50 | Person skyler, walter, flynn; 51 | Relative marie, hank; 52 | 53 | @Before 54 | public void setUp() { 55 | 56 | contactRepository.deleteAll(); 57 | 58 | this.skyler = contactRepository.save(new Person("Skyler", "White", 45)); 59 | this.walter = contactRepository.save(new Person("Walter", "White", 50)); 60 | this.flynn = contactRepository.save(new Person("Walter Jr. (Flynn)", "White", 17)); 61 | this.marie = contactRepository.save(new Relative("Marie", "Schrader", 38)); 62 | this.hank = contactRepository.save(new Relative("Hank", "Schrader", 43)); 63 | } 64 | 65 | @Test 66 | public void countByConcreteSubtypeExample() { 67 | 68 | Example example = Example.of(new Person(null, null, null)); 69 | 70 | assertThat(userRepository.count(example), is(3L)); 71 | } 72 | 73 | @Test 74 | public void findAllPersonsBySimpleExample() { 75 | 76 | Example example = Example.of(new Person(".*", null, null), // 77 | matching().withStringMatcher(StringMatcher.REGEX)); 78 | 79 | assertThat(userRepository.findAll(example), containsInAnyOrder(skyler, walter, flynn)); 80 | assertThat(userRepository.findAll(example), not(containsInAnyOrder(hank, marie))); 81 | } 82 | 83 | @Test 84 | public void findAllRelativesBySimpleExample() { 85 | 86 | Example example = Example.of(new Relative(".*", null, null), // 87 | matching().withStringMatcher(StringMatcher.REGEX)); 88 | 89 | assertThat(contactRepository.findAll(example), containsInAnyOrder(hank, marie)); 90 | assertThat(contactRepository.findAll(example), not(containsInAnyOrder(skyler, walter, flynn))); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/test/java/example/MongoOperationsIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package example; 18 | 19 | import static org.hamcrest.CoreMatchers.*; 20 | import static org.junit.Assert.*; 21 | import static org.springframework.data.domain.ExampleMatcher.*; 22 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*; 23 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith; 24 | import static org.springframework.data.mongodb.core.query.Criteria.*; 25 | import static org.springframework.data.mongodb.core.query.Query.*; 26 | 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.data.domain.Example; 33 | import org.springframework.data.domain.ExampleMatcher.*; 34 | import org.springframework.data.mongodb.core.MongoOperations; 35 | import org.springframework.data.mongodb.core.query.Query; 36 | import org.springframework.test.context.junit4.SpringRunner; 37 | 38 | /** 39 | * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories. 40 | * 41 | * @author Mark Paluch 42 | * @author Oliver Gierke 43 | */ 44 | @RunWith(SpringRunner.class) 45 | @SpringBootTest 46 | public class MongoOperationsIntegrationTests { 47 | 48 | @Autowired MongoOperations operations; 49 | 50 | Person skyler, walter, flynn, marie, hank; 51 | 52 | @Before 53 | public void setUp() { 54 | 55 | operations.remove(new Query(), Person.class); 56 | 57 | this.skyler = new Person("Skyler", "White", 45); 58 | this.walter = new Person("Walter", "White", 50); 59 | this.flynn = new Person("Walter Jr. (Flynn)", "White", 17); 60 | this.marie = new Person("Marie", "Schrader", 38); 61 | this.hank = new Person("Hank", "Schrader", 43); 62 | 63 | operations.save(this.skyler); 64 | operations.save(this.walter); 65 | operations.save(this.flynn); 66 | operations.save(this.marie); 67 | operations.save(this.hank); 68 | } 69 | 70 | @Test 71 | public void ignoreNullProperties() { 72 | 73 | Query query = query(byExample(new Person(null, null, 17))); 74 | 75 | assertThat(operations.find(query, Person.class), hasItems(flynn)); 76 | } 77 | 78 | @Test 79 | public void substringMatching() { 80 | 81 | Example example = Example.of(new Person("er", null, null), matching().// 82 | withStringMatcher(StringMatcher.ENDING)); 83 | 84 | assertThat(operations.find(query(byExample(example)), Person.class), hasItems(skyler, walter)); 85 | } 86 | 87 | @Test 88 | public void regexMatching() { 89 | 90 | Example example = Example.of(new Person("(Skyl|Walt)er", null, null), matching().// 91 | withMatcher("firstname", matcher -> matcher.regex())); 92 | 93 | assertThat(operations.find(query(byExample(example)), Person.class), hasItems(skyler, walter)); 94 | } 95 | 96 | @Test 97 | public void matchStartingStringsIgnoreCase() { 98 | 99 | Example example = Example.of(new Person("Walter", "WHITE", null), 100 | matching(). // 101 | withIgnorePaths("age").// 102 | withMatcher("firstname", startsWith()).// 103 | withMatcher("lastname", ignoreCase())); 104 | 105 | assertThat(operations.find(query(byExample(example)), Person.class), hasItems(flynn, walter)); 106 | } 107 | 108 | @Test 109 | public void configuringMatchersUsingLambdas() { 110 | 111 | Example example = Example.of(new Person("Walter", "WHITE", null), 112 | matching().// 113 | withIgnorePaths("age"). // 114 | withMatcher("firstname", matcher -> matcher.startsWith()). // 115 | withMatcher("lastname", matcher -> matcher.ignoreCase())); 116 | 117 | assertThat(operations.find(query(byExample(example)), Person.class), hasItems(flynn, walter)); 118 | } 119 | 120 | @Test 121 | public void valueTransformer() { 122 | 123 | Example example = Example.of(new Person(null, "White", 99), matching(). // 124 | withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)))); 125 | 126 | assertThat(operations.find(query(byExample(example)), Person.class), hasItems(walter)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /mongodb-query-by-example/src/test/java/example/UserRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | import static org.springframework.data.domain.ExampleMatcher.*; 21 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.*; 22 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith; 23 | 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.data.domain.Example; 30 | import org.springframework.data.domain.ExampleMatcher.StringMatcher; 31 | import org.springframework.test.context.junit4.SpringRunner; 32 | 33 | /** 34 | * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories. 35 | * 36 | * @author Mark Paluch 37 | * @author Oliver Gierke 38 | */ 39 | @SuppressWarnings("unused") 40 | @RunWith(SpringRunner.class) 41 | @SpringBootTest 42 | public class UserRepositoryIntegrationTests { 43 | 44 | @Autowired UserRepository repository; 45 | 46 | Person skyler, walter, flynn, marie, hank; 47 | 48 | @Before 49 | public void setUp() { 50 | 51 | repository.deleteAll(); 52 | 53 | this.skyler = repository.save(new Person("Skyler", "White", 45)); 54 | this.walter = repository.save(new Person("Walter", "White", 50)); 55 | this.flynn = repository.save(new Person("Walter Jr. (Flynn)", "White", 17)); 56 | this.marie = repository.save(new Person("Marie", "Schrader", 38)); 57 | this.hank = repository.save(new Person("Hank", "Schrader", 43)); 58 | } 59 | 60 | @Test 61 | public void countBySimpleExample() { 62 | 63 | Example example = Example.of(new Person(null, "White", null)); 64 | 65 | assertThat(repository.count(example), is(3L)); 66 | } 67 | 68 | @Test 69 | public void ignorePropertiesAndMatchByAge() { 70 | 71 | Example example = Example.of(flynn, matching(). // 72 | withIgnorePaths("firstname", "lastname")); 73 | 74 | assertThat(repository.findOne(example), is(flynn)); 75 | } 76 | 77 | @Test 78 | public void substringMatching() { 79 | 80 | Example example = Example.of(new Person("er", null, null), matching().// 81 | withStringMatcher(StringMatcher.ENDING)); 82 | 83 | assertThat(repository.findAll(example), hasItems(skyler, walter)); 84 | } 85 | 86 | @Test 87 | public void regexMatching() { 88 | 89 | Example example = Example.of(new Person("(Skyl|Walt)er", null, null), matching().// 90 | withMatcher("firstname", matcher -> matcher.regex())); 91 | 92 | assertThat(repository.findAll(example), hasItems(skyler, walter)); 93 | } 94 | 95 | @Test 96 | public void matchStartingStringsIgnoreCase() { 97 | 98 | Example example = Example.of(new Person("Walter", "WHITE", null), 99 | matching().// 100 | withIgnorePaths("age").// 101 | withMatcher("firstname", startsWith()).// 102 | withMatcher("lastname", ignoreCase())); 103 | 104 | assertThat(repository.findAll(example), hasItems(flynn, walter)); 105 | } 106 | 107 | @Test 108 | public void configuringMatchersUsingLambdas() { 109 | 110 | Example example = Example.of(new Person("Walter", "WHITE", null), 111 | matching().// 112 | withIgnorePaths("age").// 113 | withMatcher("firstname", matcher -> matcher.startsWith()).// 114 | withMatcher("lastname", matcher -> matcher.ignoreCase())); 115 | 116 | assertThat(repository.findAll(example), hasItems(flynn, walter)); 117 | } 118 | 119 | @Test 120 | public void valueTransformer() { 121 | 122 | Example example = Example.of(new Person(null, "White", 99), matching(). // 123 | withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)))); 124 | 125 | assertThat(repository.findAll(example), hasItems(walter)); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /mongodb-reactive/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | com.springone.2016 7 | whats-new-in-spring-data 8 | 1.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | spring-data-mongodb-reactive 12 | What's new in Spring Data? - MongoDB Reactive Data Access 13 | 14 | 15 | 16 | 17 | org.springframework.data 18 | spring-data-commons 19 | 2.0.0.DATACMNS-836-SNAPSHOT 20 | 21 | 22 | 23 | org.springframework.data 24 | spring-data-mongodb 25 | 2.0.0.DATAMONGO-1444-SNAPSHOT 26 | 27 | 28 | 29 | org.mongodb 30 | mongodb-driver-reactivestreams 31 | 1.2.0 32 | 33 | 34 | 35 | org.mongodb 36 | mongo-java-driver 37 | 3.2.2 38 | 39 | 40 | 41 | io.projectreactor 42 | reactor-core 43 | 3.0.0.RC1 44 | 45 | 46 | 47 | io.reactivex 48 | rxjava 49 | 1.1.5 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-data-mongodb 55 | 56 | 57 | 58 | de.flapdoodle.embed 59 | de.flapdoodle.embed.mongo 60 | runtime 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /mongodb-reactive/src/main/java/example/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.bson.types.ObjectId; 19 | import org.springframework.data.annotation.Id; 20 | import org.springframework.data.mongodb.core.mapping.Document; 21 | 22 | import lombok.Data; 23 | import lombok.NoArgsConstructor; 24 | 25 | /** 26 | * Sample user class. 27 | * 28 | * @author Mark Paluch 29 | */ 30 | @Data 31 | @Document(collection = "contacts") 32 | @NoArgsConstructor 33 | public class Person { 34 | 35 | @Id private ObjectId objectId; 36 | private String firstname; 37 | private String lastname; 38 | private Integer age; 39 | 40 | public Person(String firstname, String lastname, Integer age) { 41 | this.firstname = firstname; 42 | this.lastname = lastname; 43 | this.age = age; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mongodb-reactive/src/main/java/example/ReactivePersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.bson.types.ObjectId; 19 | import org.springframework.data.repository.NoRepositoryBean; 20 | import org.springframework.data.repository.Repository; 21 | 22 | import reactor.core.publisher.Mono; 23 | import rx.Observable; 24 | import rx.Single; 25 | 26 | /** 27 | * Reactive Repository using Project Reactor and RxJava types. 28 | * 29 | * @author Mark Paluch 30 | */ 31 | @NoRepositoryBean 32 | public interface ReactivePersonRepository extends Repository { 33 | 34 | /** 35 | * Find one by {@code firstname}, 36 | * 37 | * @param firstname 38 | * @param lastname 39 | * @return an {@link Single} 40 | */ 41 | Mono findByFirstnameAndLastname(String firstname, String lastname); 42 | 43 | /** 44 | * Find many by {@code lastname}. 45 | * 46 | * @param lastname 47 | * @return an {@link Observable} 48 | */ 49 | Observable findByLastname(String lastname); 50 | 51 | /** 52 | * Find one by {@code firstname} wrapped inside a {@link Mono} and {@code lastname}. 53 | * 54 | * @param firstname 55 | * @param lastname 56 | * @return a {@link Mono} 57 | */ 58 | Mono findByFirstnameAndLastname(Mono firstname, String lastname); 59 | 60 | /** 61 | * Find one by {@code id} wrapped inside a {@link Mono}. 62 | * 63 | * @param objectId 64 | * @return 65 | */ 66 | Mono findOne(Mono objectId); 67 | 68 | /** 69 | * Find one by {@code id} wrapped inside a {@link Single}. 70 | * 71 | * @param objectId 72 | * @return 73 | */ 74 | Single findOne(Single objectId); 75 | 76 | Mono deleteAll(); 77 | 78 | Mono save(Person person); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /mongodb-reactive/src/main/java/example/ReactorPersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.bson.types.ObjectId; 19 | import org.springframework.data.repository.NoRepositoryBean; 20 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 21 | 22 | import reactor.core.publisher.Flux; 23 | import reactor.core.publisher.Mono; 24 | 25 | /** 26 | * Reactive Repository using Project Reactor types like {@link Flux} and {@link Mono}. 27 | * 28 | * @author Mark Paluch 29 | */ 30 | @NoRepositoryBean 31 | public interface ReactorPersonRepository extends ReactiveCrudRepository { 32 | 33 | /** 34 | * Find one by {@code firstname} and {@code lastname}. 35 | * 36 | * @param firstname 37 | * @param lastname 38 | * @return a {@link Mono} 39 | */ 40 | Mono findByFirstnameAndLastname(String firstname, String lastname); 41 | 42 | /** 43 | * Find many by {@code lastname}. 44 | * 45 | * @param lastname 46 | * @return a {@link Flux} 47 | */ 48 | Flux findByLastname(String lastname); 49 | 50 | /** 51 | * Find many by {@code firstname} wrapped inside a {@link Mono}. 52 | * 53 | * @param lastname 54 | * @return a {@link Flux} 55 | */ 56 | Flux findByLastname(Mono lastname); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /mongodb-reactive/src/main/java/example/RxJavaPersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.bson.types.ObjectId; 19 | import org.springframework.data.repository.NoRepositoryBean; 20 | import org.springframework.data.repository.reactive.RxJavaCrudRepository; 21 | 22 | import rx.Observable; 23 | import rx.Single; 24 | 25 | /** 26 | * Reactive Repository using RxJava types like {@link Observable} and {@link Single}. 27 | * 28 | * @author Mark Paluch 29 | */ 30 | @NoRepositoryBean 31 | public interface RxJavaPersonRepository extends RxJavaCrudRepository { 32 | 33 | /** 34 | * Find one by {@code firstname} and {@code lastname}. 35 | * 36 | * @param firstname 37 | * @param lastname 38 | * @return a {@link Single} 39 | */ 40 | Single findByFirstnameAndLastname(String firstname, String lastname); 41 | 42 | /** 43 | * Find many by {@code lastname}. 44 | * 45 | * @param lastname 46 | * @return an {@link Observable} 47 | */ 48 | Observable findByLastname(String lastname); 49 | 50 | /** 51 | * Find many by {@code firstname} wrapped inside a {@link Single}. 52 | * 53 | * @param lastname 54 | * @return an {@link Observable} 55 | */ 56 | Observable findByLastname(Single lastname); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/java/example/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.beans.BeansException; 19 | import org.springframework.beans.factory.BeanClassLoaderAware; 20 | import org.springframework.beans.factory.BeanFactory; 21 | import org.springframework.beans.factory.BeanFactoryAware; 22 | import org.springframework.beans.factory.annotation.Value; 23 | import org.springframework.boot.autoconfigure.SpringBootApplication; 24 | import org.springframework.context.annotation.Bean; 25 | import org.springframework.data.mongodb.core.ReactiveMongoDbFactory; 26 | import org.springframework.data.mongodb.core.ReactiveMongoOperations; 27 | import org.springframework.data.mongodb.core.ReactiveMongoTemplate; 28 | import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener; 29 | import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; 30 | import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; 31 | import org.springframework.data.repository.query.DefaultEvaluationContextProvider; 32 | 33 | import com.mongodb.reactivestreams.client.MongoClient; 34 | import com.mongodb.reactivestreams.client.MongoClients; 35 | import com.mongodb.reactivestreams.client.MongoDatabase; 36 | 37 | /** 38 | * @author Mark Paluch 39 | */ 40 | @SpringBootApplication 41 | public class ApplicationConfiguration implements BeanClassLoaderAware, BeanFactoryAware { 42 | 43 | @Value("${mongo.database:reactive}") private String database; 44 | 45 | private ClassLoader classLoader; 46 | private BeanFactory beanFactory; 47 | 48 | @Bean 49 | MongoClient mongoClient() { 50 | return MongoClients.create(); 51 | } 52 | 53 | @Bean 54 | MongoDatabase mongoDatabase(MongoClient mongoClient) { 55 | return mongoClient.getDatabase(database); 56 | } 57 | 58 | @Bean 59 | ReactiveMongoDbFactory reactiveMongoDbFactory(MongoClient mongoClient) { 60 | return new ReactiveMongoDbFactory(mongoClient, database); 61 | } 62 | 63 | @Bean 64 | ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDbFactory mongoDbFactory) { 65 | return new ReactiveMongoTemplate(mongoDbFactory); 66 | } 67 | 68 | @Bean 69 | ReactiveMongoRepositoryFactory reactiveMongoRepositoryFactory(ReactiveMongoOperations reactiveMongoOperations) { 70 | 71 | ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(reactiveMongoOperations); 72 | factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); 73 | factory.setBeanClassLoader(classLoader); 74 | factory.setBeanFactory(beanFactory); 75 | factory.setEvaluationContextProvider(DefaultEvaluationContextProvider.INSTANCE); 76 | 77 | return factory; 78 | } 79 | 80 | @Bean 81 | RxJavaPersonRepository rxJavaPersonRepository(ReactiveMongoRepositoryFactory factory) { 82 | return factory.getRepository(RxJavaPersonRepository.class); 83 | } 84 | 85 | @Bean 86 | ReactorPersonRepository reactivePersonRepository(ReactiveMongoRepositoryFactory factory) { 87 | return factory.getRepository(ReactorPersonRepository.class); 88 | } 89 | 90 | @Bean 91 | ReactivePersonRepository mixedReactivePersonRepository(ReactiveMongoRepositoryFactory factory) { 92 | return factory.getRepository(ReactivePersonRepository.class); 93 | } 94 | 95 | @Override 96 | public void setBeanClassLoader(ClassLoader classLoader) { 97 | this.classLoader = classLoader; 98 | } 99 | 100 | @Override 101 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 102 | this.beanFactory = beanFactory; 103 | } 104 | 105 | public @Bean LoggingEventListener mongoEventListener() { 106 | return new LoggingEventListener(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/java/example/ReactiveMongoOperationsIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.test.context.SpringBootTest; 26 | import org.springframework.data.mongodb.core.ReactiveMongoOperations; 27 | import org.springframework.data.mongodb.core.query.Query; 28 | import org.springframework.test.context.junit4.SpringRunner; 29 | 30 | import reactor.core.publisher.Flux; 31 | import reactor.core.publisher.Mono; 32 | import reactor.test.TestSubscriber; 33 | 34 | /** 35 | * Integration tests showing the usage of Reactive MongoDB support through 36 | * {@link org.springframework.data.mongodb.core.ReactiveMongoOperations}. 37 | * 38 | * @author Mark Paluch 39 | */ 40 | @SuppressWarnings("unused") 41 | @RunWith(SpringRunner.class) 42 | @SpringBootTest 43 | public class ReactiveMongoOperationsIntegrationTests { 44 | 45 | @Autowired ReactiveMongoOperations operations; 46 | 47 | Person skyler, walter, flynn; 48 | 49 | @Before 50 | public void setUp() { 51 | TestSubscriber testSubscriber = TestSubscriber.subscribe(operations.dropCollection(Person.class)); 52 | testSubscriber.await(); 53 | 54 | this.skyler = subscribe(operations.save(new Person("Skyler", "White", 45))); 55 | this.walter = subscribe(operations.save(Mono.just(new Person("Walter", "White", 50)))); 56 | } 57 | 58 | @Test 59 | public void shouldReturnCount() { 60 | 61 | Mono count = operations.count(new Query(), Person.class); 62 | 63 | assertThat(count.block(), is(2L)); 64 | } 65 | 66 | @Test 67 | public void insertCountAndDeleteChain() { 68 | 69 | flynn = new Person("Walter Jr. (Flynn)", "White", 17); 70 | 71 | Flux flux = operations.insert(Mono.just(flynn))// 72 | .then(() -> { 73 | return operations.count(new Query(), Person.class); 74 | }) // 75 | .flatMap(count -> { 76 | System.out.println("Count: " + count); 77 | assertThat(count, is(3L)); 78 | return operations.remove(flynn); 79 | }) // 80 | .flatMap(deleteResult -> operations.count(new Query(), Person.class)) // 81 | .doOnNext(count -> { 82 | System.out.println("Count: " + count); 83 | assertThat(count, is(2L)); 84 | }); 85 | 86 | flux.then().block(); 87 | } 88 | 89 | private T subscribe(Mono mono) { 90 | return mono.block(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/java/example/ReactivePersonRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.test.context.SpringBootTest; 28 | import org.springframework.test.context.junit4.SpringRunner; 29 | 30 | import reactor.core.publisher.Flux; 31 | import reactor.core.publisher.Mono; 32 | import reactor.test.TestSubscriber; 33 | import rx.Observable; 34 | import rx.Single; 35 | 36 | /** 37 | * Integration tests showing the usage of Reactive MongoDB support using mixed reactive types through Spring Data 38 | * repositories. 39 | * 40 | * @author Mark Paluch 41 | */ 42 | @SuppressWarnings("unused") 43 | @RunWith(SpringRunner.class) 44 | @SpringBootTest 45 | public class ReactivePersonRepositoryIntegrationTests { 46 | 47 | @Autowired ReactivePersonRepository repository; 48 | 49 | Person skyler, walter, flynn; 50 | 51 | @Before 52 | public void setUp() { 53 | TestSubscriber testSubscriber = TestSubscriber.subscribe(repository.deleteAll()); 54 | testSubscriber.await(); 55 | 56 | this.skyler = subscribe(repository.save(new Person("Skyler", "White", 45))); 57 | this.walter = subscribe(repository.save(new Person("Walter", "White", 50))); 58 | this.flynn = subscribe(repository.save(new Person("Walter Jr. (Flynn)", "White", 17))); 59 | } 60 | 61 | @Test 62 | public void shouldFindByLastname() { 63 | 64 | Observable whites = repository.findByLastname("White"); 65 | 66 | List result = whites.toList().toBlocking().single(); 67 | 68 | assertThat(result, hasItems(skyler, walter, flynn)); 69 | } 70 | 71 | @Test 72 | public void shouldFindByFirstnameAndLastname() { 73 | 74 | Mono walter = repository.findByFirstnameAndLastname("Walter", "White"); 75 | 76 | assertThat(walter.block(), is(this.walter)); 77 | } 78 | 79 | /** 80 | * Parameters can be wrapped inside of {@link Mono}, {@link Flux}, or {@link org.reactivestreams.Publisher}. 81 | * Parameters are unwrapped upon subscription to the result type. 82 | */ 83 | @Test 84 | public void shouldFindByWrappedLastname() { 85 | 86 | Mono walter = repository.findByFirstnameAndLastname(Mono.just("Walter"), "White"); 87 | 88 | TestSubscriber testSubscriber = TestSubscriber.subscribe(walter); 89 | 90 | testSubscriber.await(); 91 | testSubscriber.assertNoError(); 92 | testSubscriber.assertValues(this.walter); 93 | } 94 | 95 | /** 96 | * Reactive Repositories allow redeclaration of base methods and wrapper type conversion. 97 | */ 98 | @Test 99 | public void findOne() { 100 | 101 | Single findOneRxJava = repository.findOne(Single.just(skyler.getObjectId())); 102 | Person skylerFromRxJava = findOneRxJava.toBlocking().value(); 103 | 104 | assertThat(skylerFromRxJava, is(skyler)); 105 | 106 | Mono findOneReactor = repository.findOne(Mono.just(skyler.getObjectId())); 107 | Person skylerFromRxReactor = findOneReactor.block(); 108 | 109 | assertThat(skylerFromRxReactor, is(skyler)); 110 | } 111 | 112 | private T subscribe(Mono mono) { 113 | return mono.block(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/java/example/ReactorPersonRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.assertj.core.util.Sets; 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.test.context.SpringBootTest; 27 | import org.springframework.test.context.junit4.SpringRunner; 28 | 29 | import reactor.core.publisher.Flux; 30 | import reactor.core.publisher.Mono; 31 | import reactor.test.TestSubscriber; 32 | 33 | /** 34 | * Integration tests showing the usage of Reactive MongoDB support using Project Reactor types through Spring Data 35 | * repositories. 36 | * 37 | * @author Mark Paluch 38 | */ 39 | @SuppressWarnings("unused") 40 | @RunWith(SpringRunner.class) 41 | @SpringBootTest 42 | public class ReactorPersonRepositoryIntegrationTests { 43 | 44 | @Autowired ReactorPersonRepository repository; 45 | 46 | Person skyler, walter, flynn; 47 | 48 | @Before 49 | public void setUp() { 50 | TestSubscriber testSubscriber = TestSubscriber.subscribe(repository.deleteAll()); 51 | testSubscriber.await(); 52 | 53 | this.skyler = subscribe(repository.save(new Person("Skyler", "White", 45))); 54 | this.walter = subscribe(repository.save(new Person("Walter", "White", 50))); 55 | this.flynn = subscribe(repository.save(new Person("Walter Jr. (Flynn)", "White", 17))); 56 | } 57 | 58 | /** 59 | * Observables are handles to an execution. Nothing happens without a subscription to a 60 | * {@link org.reactivestreams.Publisher}. 61 | */ 62 | @Test 63 | public void shouldExecuteOnlyOnSubscribe() { 64 | 65 | assertThat(subscribe(repository.count()), is(3L)); 66 | 67 | repository.save(new Person("Marie", "Schrader", 38)); 68 | repository.save(new Person("Hank", "Schrader", 43)); 69 | 70 | assertThat(subscribe(repository.count()), is(3L)); 71 | } 72 | 73 | /** 74 | * {@link Flux} execution can emit zero, one or many items. Declaring {@link Flux} as return type translates to a 75 | * MongoDB {@code find} execution. 76 | */ 77 | @Test 78 | public void shouldFindByLastname() { 79 | 80 | Flux whites = repository.findByLastname("White"); 81 | 82 | TestSubscriber testSubscriber = TestSubscriber.subscribe(whites.doOnNext(System.out::println)); 83 | 84 | testSubscriber.await(); 85 | testSubscriber.assertNoError(); 86 | testSubscriber.assertValueCount(3); 87 | testSubscriber.assertContainValues(Sets.newLinkedHashSet(skyler, walter, flynn)); 88 | } 89 | 90 | /** 91 | * {@link Mono} execution can emit zero or one item. Declaring {@link Mono} as return type translates to a MongoDB 92 | * {@code findOne} execution. 93 | */ 94 | @Test 95 | public void shouldFindByFirstnameAndLastname() { 96 | 97 | Mono walter = repository.findByFirstnameAndLastname("Walter", "White"); 98 | 99 | TestSubscriber testSubscriber = TestSubscriber.subscribe(walter); 100 | 101 | testSubscriber.await(); 102 | testSubscriber.assertNoError(); 103 | testSubscriber.awaitAndAssertNextValues(this.walter); 104 | } 105 | 106 | /** 107 | * Parameters can be wrapped inside of {@link Mono}, {@link Flux}, or {@link org.reactivestreams.Publisher}. 108 | * Parameters are unwrapped upon subscription to the result type. 109 | */ 110 | @Test 111 | public void shouldFindByWrappedLastname() { 112 | 113 | Flux whites = repository.findByLastname(Mono.just("White")); 114 | 115 | TestSubscriber testSubscriber = TestSubscriber.subscribe(whites); 116 | 117 | testSubscriber.await(); 118 | testSubscriber.assertNoError(); 119 | testSubscriber.assertValueCount(3); 120 | testSubscriber.assertContainValues(Sets.newLinkedHashSet(skyler, walter, flynn)); 121 | } 122 | 123 | private T subscribe(Mono mono) { 124 | return mono.block(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/java/example/RxJavaPersonRepositoryIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.test.context.SpringBootTest; 26 | import org.springframework.test.context.junit4.SpringRunner; 27 | 28 | import rx.Observable; 29 | import rx.Single; 30 | import rx.observers.TestSubscriber; 31 | 32 | /** 33 | * Integration tests showing the usage of Reactive MongoDB support using RxJava types through Spring Data repositories. 34 | * 35 | * @author Mark Paluch 36 | */ 37 | @SuppressWarnings("unused") 38 | @RunWith(SpringRunner.class) 39 | @SpringBootTest 40 | public class RxJavaPersonRepositoryIntegrationTests { 41 | 42 | @Autowired RxJavaPersonRepository repository; 43 | 44 | Person skyler, walter, flynn; 45 | 46 | @Before 47 | public void setUp() { 48 | TestSubscriber testSubscriber = new TestSubscriber<>(); 49 | repository.deleteAll().subscribe(testSubscriber); 50 | testSubscriber.awaitTerminalEvent(); 51 | 52 | this.skyler = subscribe(repository.save(new Person("Skyler", "White", 45))); 53 | this.walter = subscribe(repository.save(new Person("Walter", "White", 50))); 54 | this.flynn = subscribe(repository.save(new Person("Walter Jr. (Flynn)", "White", 17))); 55 | } 56 | 57 | /** 58 | * Observables are handles to an execution. Nothing happens without a subscription to a {@link Observable}. 59 | */ 60 | @Test 61 | public void shouldExecuteOnlyOnSubscribe() { 62 | 63 | assertThat(subscribe(repository.count()), is(3L)); 64 | 65 | repository.save(new Person("Marie", "Schrader", 38)); 66 | repository.save(new Person("Hank", "Schrader", 43)); 67 | 68 | assertThat(subscribe(repository.count()), is(3L)); 69 | } 70 | 71 | /** 72 | * {@link Observable} execution can emit zero, one or many items. Declaring {@link Observable} as return type 73 | * translates to a MongoDB {@code find} execution. 74 | */ 75 | @Test 76 | public void shouldFindByLastname() { 77 | 78 | Observable whites = repository.findByLastname("White"); 79 | 80 | TestSubscriber testSubscriber = new TestSubscriber<>(); 81 | 82 | whites.doOnNext(System.out::println).subscribe(testSubscriber); 83 | 84 | testSubscriber.awaitTerminalEvent(); 85 | testSubscriber.assertNoErrors(); 86 | testSubscriber.assertValueCount(3); 87 | 88 | assertThat(testSubscriber.getOnNextEvents(), hasItems(skyler, walter, flynn)); 89 | } 90 | 91 | /** 92 | * {@link Single} execution can emit zero or one item. Declaring {@link Single} as return type translates to a MongoDB 93 | * {@code findOne} execution. 94 | */ 95 | @Test 96 | public void shouldFindByFirstnameAndLastname() { 97 | 98 | Single walter = repository.findByFirstnameAndLastname("Walter", "White"); 99 | 100 | TestSubscriber testSubscriber = new TestSubscriber<>(); 101 | 102 | walter.subscribe(testSubscriber); 103 | 104 | testSubscriber.awaitTerminalEvent(); 105 | testSubscriber.assertNoErrors(); 106 | 107 | testSubscriber.assertValueCount(1); 108 | 109 | assertThat(testSubscriber.getOnNextEvents(), hasItems(this.walter)); 110 | } 111 | 112 | /** 113 | * Parameters can be wrapped inside of {@link Single} or {@link Observable}. Parameters are unwrapped upon 114 | * subscription to the result type. 115 | */ 116 | @Test 117 | public void shouldFindByWrappedLastname() { 118 | 119 | Observable whites = repository.findByLastname(Single.just("White")); 120 | 121 | TestSubscriber testSubscriber = new TestSubscriber<>(); 122 | 123 | whites.subscribe(testSubscriber); 124 | 125 | testSubscriber.awaitTerminalEvent(); 126 | testSubscriber.assertNoErrors(); 127 | 128 | testSubscriber.assertValueCount(3); 129 | 130 | assertThat(testSubscriber.getOnNextEvents(), hasItems(skyler, walter, flynn)); 131 | } 132 | 133 | private T subscribe(Single single) { 134 | return single.toBlocking().value(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /mongodb-reactive/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | logging.level.org.springframework.data.mongodb=INFO 3 | logging.level.org.springframework.boot.autoconfigure.mongo.embedded=WARN 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.springone.2016 6 | whats-new-in-spring-data 7 | 1.0.0.BUILD-SNAPSHOT 8 | pom 9 | 10 | What's new in Spring Data? 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 1.4.0.RELEASE 16 | 17 | 18 | 19 | jpa-query-by-example 20 | mongodb-query-by-example 21 | jpa-projections 22 | mongodb-composed-annotations 23 | jpa-query-method-wrapped-parameters 24 | redis-cluster 25 | redis-repositories 26 | mongodb-reactive 27 | 28 | 29 | 30 | 31 | 1.1.3 32 | 1.8 33 | 4.0.9 34 | Ingalls-M1 35 | 36 | 37 | 38 | 39 | 40 | cstrobl 41 | Christoph Strobl 42 | cstrobl@pivotal.io 43 | 44 | 45 | mpaluch 46 | Mark Paluch 47 | mpaluch@pivotal.io 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.projectlombok 55 | lombok 56 | 1.16.6 57 | provided 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-devtools 63 | true 64 | 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-test 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-compiler-plugin 81 | 82 | ${java.version} 83 | ${java.version} 84 | -parameters 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | spring-libs-snapshot 94 | https://repo.spring.io/libs-snapshot 95 | 96 | 97 | 98 | 99 | 100 | spring-libs-snapshot 101 | https://repo.spring.io/libs-snapshot 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /readme.adoc: -------------------------------------------------------------------------------- 1 | = What's new in Spring Data? 2 | 3 | image:https://travis-ci.org/mp911de/whats-new-in-spring-data.svg?branch=master["Build Status", link="https://travis-ci.org/mp911de/whats-new-in-spring-data"] 4 | 5 | This repository contains code for the https://2016.event.springoneplatform.io/schedule/sessions/what_s_new_in_spring_data.html[What's New in Spring Data?] talk. It will give you a broad overview of the new features introduced in the latest Spring Data release trains. 6 | 7 | 8 | == Building the project 9 | 10 | === Prerequisites 11 | 12 | - Maven 3 13 | - Java 8 14 | 15 | ``` 16 | $ git clone https://github.com/SpringOnePlatform2016/whats-new-in-spring-data.git 17 | $ cd whats-new-in-spring-data 18 | $ make -C redis-cluster start 19 | $ mvn clean install 20 | ``` 21 | 22 | ## Getting Help 23 | 24 | The main project http://projects.spring.io/spring-data/[website] contains links to basic project information such as source code, JavaDocs, Issue tracking, etc. 25 | 26 | For more detailed questions, please refer to http://stackoverflow.com/questions/tagged/spring-data[spring-data on stackoverflow]. If you are new to Spring as well as to Spring Data, look for information about https://spring.io/projects[Spring projects]. 27 | -------------------------------------------------------------------------------- /redis-cluster/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /work/ 3 | dump.rdb 4 | -------------------------------------------------------------------------------- /redis-cluster/Makefile: -------------------------------------------------------------------------------- 1 | 2 | REDIS_VERSION:=3.2.0 3 | 4 | ######### 5 | # Cluster 6 | ######### 7 | .PRECIOUS: work/cluster-%.conf 8 | 9 | work/cluster-%.conf: 10 | @mkdir -p $(@D) 11 | 12 | echo port $* >> $@ 13 | echo cluster-enabled yes >> $@ 14 | echo cluster-config-file $(shell pwd)/work/nodes-$*.conf >> $@ 15 | echo cluster-node-timeout 5 >> $@ 16 | echo pidfile $(shell pwd)/work/cluster-$*.pid >> $@ 17 | echo logfile $(shell pwd)/work/cluster-$*.log >> $@ 18 | echo save \"\" >> $@ 19 | 20 | work/cluster-%.pid: work/cluster-%.conf work/redis/bin/redis-server 21 | work/redis/bin/redis-server $< & 22 | 23 | cluster-start: work/cluster-7379.pid work/cluster-7380.pid work/cluster-7381.pid work/cluster-7382.pid 24 | 25 | work/meet-%: 26 | -work/redis/bin/redis-cli -p $* cluster meet 127.0.0.1 7379 27 | 28 | # Handled separately because this node is a slave 29 | work/meet-7381: 30 | -work/redis/bin/redis-cli -p 7381 cluster meet 127.0.0.1 7379 31 | sleep 2 32 | -work/redis/bin/redis-cli -p 7381 cluster replicate $(shell work/redis/bin/redis-cli -p 7379 cluster myid) 33 | 34 | # Handled separately because this node is a slave 35 | work/meet-7382: 36 | -work/redis/bin/redis-cli -p 7382 cluster meet 127.0.0.1 7379 37 | sleep 2 38 | -work/redis/bin/redis-cli -p 7382 cluster replicate $(shell work/redis/bin/redis-cli -p 7380 cluster myid) 39 | 40 | cluster-meet: work/meet-7380 work/meet-7381 work/meet-7382 41 | 42 | cluster-stop: stop-7379 stop-7380 stop-7381 stop-7382 43 | 44 | cluster-slots: 45 | -work/redis/bin/redis-cli -p 7379 cluster addslots $(shell seq 0 8191) 46 | -work/redis/bin/redis-cli -p 7380 cluster addslots $(shell seq 8192 16383) 47 | 48 | cluster-init: cluster-start cluster-meet cluster-slots 49 | 50 | ######## 51 | # Global 52 | ######## 53 | clean: 54 | rm -rf work/*.conf work/*.log 55 | 56 | clobber: 57 | rm -rf work 58 | 59 | work/redis/bin/redis-cli work/redis/bin/redis-server: 60 | @mkdir -p work/redis 61 | 62 | curl -sSL https://github.com/antirez/redis/archive/$(REDIS_VERSION).tar.gz | tar xzf - -C work 63 | $(MAKE) -C work/redis-$(REDIS_VERSION) -j 64 | $(MAKE) -C work/redis-$(REDIS_VERSION) PREFIX=$(shell pwd)/work/redis install 65 | rm -rf work/redis-$(REDIS_VERSION) 66 | 67 | start: cluster-init 68 | 69 | stop-%: work/redis/bin/redis-cli 70 | -work/redis/bin/redis-cli -p $* shutdown 71 | 72 | stop: cluster-stop 73 | -------------------------------------------------------------------------------- /redis-cluster/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | spring-data-redis-cluster-support 6 | What's new in Spring Data? - Redis Cluster support 7 | 8 | 9 | com.springone.2016 10 | whats-new-in-spring-data 11 | 1.0.0.BUILD-SNAPSHOT 12 | 13 | 14 | 15 | 2.9.0 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-redis 23 | 24 | 25 | 26 | org.springframework.data 27 | spring-data-redis 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /redis-cluster/readme.md: -------------------------------------------------------------------------------- 1 | # Spring Data Redis - Cluster Examples # 2 | 3 | This project contains Redis 3 Cluster specific features of Spring Data Redis. 4 | 5 | To run the code in this sample a running cluster environment is required. Please refer to the [redis cluster-tutorial](http://redis.io/topics/cluster-tutorial) for detailed information or check the Cluster Setup section below. 6 | 7 | ## Support for Cluster ## 8 | 9 | Cluster Support uses the same building blocks as the non clustered counterpart. We use `application.properties` to point to an initial set of known cluster nodes which will be picked up by the auto configuration. 10 | 11 | ```properties 12 | spring.redis.cluster.nodes[0]=127.0.0.1:7379 13 | spring.redis.cluster.nodes[1]=127.0.0.1:7380 14 | spring.redis.cluster.nodes[2]=127.0.0.1:7381 15 | ``` 16 | 17 | **INFORMATION:** The tests flush the db of all known instances during the JUnit _setup_ phase to allow inspecting data directly on the cluster nodes after a test is run. 18 | 19 | ## Cluster Setup ## 20 | 21 | To quickly set up a cluster of 4 nodes (2 master | 2 slave) simply invoke `make start`. 22 | 23 | 24 | ```bash 25 | $ ./make start 26 | ``` 27 | 28 | It is now possible to connect to the cluster using the `redis-cli`. 29 | 30 | ```bash 31 | redis/src $ ./redis-cli -c -p 7379 32 | 127.0.0.1:7379> cluster nodes 33 | 34 | b85eeb... 127.0.0.1:7380 master - 0 1466584139197 0 connected 8192-16383 35 | 952146... 127.0.0.1:7381 slave 42b9e7... 0 1466584139197 2 connected 36 | 42b9e7... 127.0.0.1:7379 myself,master - 0 0 1 connected 0-8191 37 | 208c1b... 127.0.0.1:7382 slave b85eeb... 0 1466584139197 3 connected 38 | ``` 39 | 40 | To shutdown the cluster use the `make stop` command. 41 | 42 | ```bash 43 | $ ./make stop 44 | ``` 45 | -------------------------------------------------------------------------------- /redis-cluster/src/main/java/example/AppConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 19 | import org.springframework.data.redis.connection.RedisConnectionFactory; 20 | import org.springframework.data.redis.core.RedisTemplate; 21 | 22 | /** 23 | * Application context configuration setting up {@link RedisConnectionFactory} and {@link RedisTemplate} according to 24 | * {@link ClusterConfigurationProperties}. 25 | * 26 | * @author Christoph Strobl 27 | */ 28 | @EnableAutoConfiguration 29 | public class AppConfig { 30 | 31 | } 32 | -------------------------------------------------------------------------------- /redis-cluster/src/test/java/example/BasicUsageTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.core.Is.*; 19 | import static org.hamcrest.core.IsCollectionContaining.*; 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.Arrays; 23 | 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.dao.DataAccessException; 30 | import org.springframework.data.redis.connection.RedisConnection; 31 | import org.springframework.data.redis.core.RedisCallback; 32 | import org.springframework.data.redis.core.RedisOperations; 33 | import org.springframework.data.redis.core.RedisTemplate; 34 | import org.springframework.test.context.junit4.SpringRunner; 35 | 36 | /** 37 | * {@link BasicUsageTests} shows general usage of {@link RedisTemplate} and {@link RedisOperations} in a clustered 38 | * environment. 39 | * 40 | * @author Christoph Strobl 41 | */ 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest(classes = AppConfig.class) 44 | public class BasicUsageTests { 45 | 46 | @Autowired RedisTemplate template; 47 | 48 | @Before 49 | public void setUp() { 50 | 51 | template.execute(new RedisCallback() { 52 | 53 | @Override 54 | public String doInRedis(RedisConnection connection) throws DataAccessException { 55 | connection.flushDb(); 56 | return "FLUSHED"; 57 | } 58 | }); 59 | } 60 | 61 | /** 62 | * Operation executed on a single node and slot.
63 | * -> {@code SLOT 5798} served by {@code 127.0.0.1:7379} 64 | */ 65 | @Test 66 | public void singleSlotOperation() { 67 | 68 | template.opsForValue().set("name", "rand al'thor"); // slot 5798 69 | assertThat(template.opsForValue().get("name"), is("rand al'thor")); 70 | } 71 | 72 | /** 73 | * Operation executed on multiple nodes and slots.
74 | * -> {@code SLOT 5798} served by {@code 127.0.0.1:7379}
75 | * -> {@code SLOT 14594} served by {@code 127.0.0.1:7380} 76 | */ 77 | @Test 78 | public void multiSlotOperation() { 79 | 80 | template.opsForValue().set("name", "matrim cauthon"); // slot 5798 81 | template.opsForValue().set("nickname", "prince of the ravens"); // slot 14594 82 | 83 | assertThat(template.opsForValue().multiGet(Arrays.asList("name", "nickname")), 84 | hasItems("matrim cauthon", "prince of the ravens")); 85 | } 86 | 87 | /** 88 | * Operation executed on a single node and slot because of pinned slot key
89 | * -> {@code SLOT 5798} served by {@code 127.0.0.1:7379} 90 | */ 91 | @Test 92 | public void fixedSlotOperation() { 93 | 94 | template.opsForValue().set("{user}.name", "perrin aybara"); // slot 5474 95 | template.opsForValue().set("{user}.nickname", "wolfbrother"); // slot 5474 96 | 97 | assertThat(template.opsForValue().multiGet(Arrays.asList("{user}.name", "{user}.nickname")), 98 | hasItems("perrin aybara", "wolfbrother")); 99 | } 100 | 101 | /** 102 | * Operation executed across the cluster to retrieve cumulated result.
103 | * -> {@code KEY age} served by {@code 127.0.0.1:7379}
104 | * -> {@code KEY name} served by {@code 127.0.0.1:7379}
105 | * -> {@code KEY nickname} served by {@code 127.0.0.1:7380} 106 | */ 107 | @Test 108 | public void multiNodeOperation() { 109 | 110 | template.opsForValue().set("age", "23"); // slot 741; 111 | template.opsForValue().set("name", "rand al'thor"); // slot 5798 112 | template.opsForValue().set("nickname", "dragon reborn"); // slot 14594 113 | 114 | assertThat(template.keys("*"), hasItems("name", "nickname", "age")); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /redis-cluster/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.redis.cluster.nodes[0]=127.0.0.1:7379 2 | spring.redis.cluster.nodes[1]=127.0.0.1:7380 3 | spring.redis.cluster.nodes[2]=127.0.0.1:7381 4 | -------------------------------------------------------------------------------- /redis-repositories/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /redis-repositories/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | spring-data-redis-repository-support 6 | What's new in Spring Data? - Redis Repositories 7 | 8 | 9 | com.springone.2016 10 | whats-new-in-spring-data 11 | 1.0.0.BUILD-SNAPSHOT 12 | 13 | 14 | 15 | 2.9.0 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-redis 23 | 24 | 25 | 26 | com.github.kstyrc 27 | embedded-redis 28 | 0.6 29 | test 30 | 31 | 32 | 33 | org.springframework.data 34 | spring-data-keyvalue 35 | 1.2.0.M1 36 | 37 | 38 | 39 | org.springframework.data 40 | spring-data-redis 41 | 1.8.0.M1 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /redis-repositories/src/main/java/example/City.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import org.springframework.data.annotation.Id; 4 | import org.springframework.data.keyvalue.annotation.KeySpace; 5 | 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | class City { 12 | 13 | String name; 14 | 15 | public City(String name) { 16 | this.name = name; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /redis-repositories/src/main/java/example/Person.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.annotation.Reference; 8 | import org.springframework.data.redis.core.RedisHash; 9 | import org.springframework.data.redis.core.index.Indexed; 10 | 11 | import lombok.Data; 12 | 13 | @Data 14 | @RedisHash("persons") 15 | class Person { 16 | 17 | @Id String id; 18 | @Indexed String firstname; 19 | String lastname; 20 | 21 | Map attributes = Collections.emptyMap(); 22 | 23 | City city; 24 | 25 | @Reference Person mother; 26 | } 27 | -------------------------------------------------------------------------------- /redis-repositories/src/main/java/example/PersonRepository.java: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | interface PersonRepository extends CrudRepository { 8 | 9 | List findByFirstname(String firstname); 10 | 11 | List findByLastname(String lastname); 12 | 13 | List findByFirstnameAndLastname(String firstname, String lastname); 14 | } 15 | -------------------------------------------------------------------------------- /redis-repositories/src/test/java/example/EmbeddedRedisRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import redis.embedded.RedisServer; 19 | 20 | import java.io.IOException; 21 | 22 | import org.junit.rules.ExternalResource; 23 | 24 | /** 25 | * JUnit rule implementation to start and shut down an embedded Redis instance. 26 | * 27 | * @author Christoph Strobl 28 | * @author Oliver Gierke 29 | */ 30 | public class EmbeddedRedisRule extends ExternalResource { 31 | 32 | private RedisServer server; 33 | 34 | /* 35 | * (non-Javadoc) 36 | * @see org.junit.rules.ExternalResource#before() 37 | */ 38 | @Override 39 | protected void before() throws IOException { 40 | 41 | this.server = new RedisServer(6379); 42 | this.server.start(); 43 | } 44 | 45 | /* 46 | * (non-Javadoc) 47 | * @see org.junit.rules.ExternalResource#after() 48 | */ 49 | @Override 50 | protected void after() { 51 | this.server.stop(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /redis-repositories/src/test/java/example/RedisRepositoryTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import java.util.Arrays; 22 | 23 | import org.junit.Before; 24 | import org.junit.ClassRule; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.data.keyvalue.core.KeyValueTemplate; 30 | import org.springframework.data.redis.connection.RedisConnectionFactory; 31 | import org.springframework.data.redis.core.PartialUpdate; 32 | import org.springframework.data.redis.core.RedisOperations; 33 | import org.springframework.test.context.junit4.SpringRunner; 34 | 35 | /** 36 | * @author Thomas Darimont 37 | * @author Oliver Gierke 38 | */ 39 | @RunWith(SpringRunner.class) 40 | @SpringBootTest 41 | public class RedisRepositoryTests { 42 | 43 | public static @ClassRule EmbeddedRedisRule embeddedRedis = new EmbeddedRedisRule(); 44 | 45 | @Autowired RedisConnectionFactory redisConnectionFactory; 46 | @Autowired RedisOperations redis; 47 | @Autowired KeyValueTemplate kvTemplate; 48 | @Autowired PersonRepository repository; 49 | 50 | @Before 51 | public void setup() { 52 | kvTemplate.delete(Person.class); 53 | } 54 | 55 | @Test 56 | public void simpleFindByMultipleProperties() { 57 | 58 | Person egwene = new Person(); 59 | egwene.firstname = "egwene"; 60 | egwene.lastname = "al'vere"; 61 | egwene.city = new City("new york"); 62 | 63 | Person marin = new Person(); 64 | marin.firstname = "marin"; 65 | marin.lastname = "al'vere"; 66 | 67 | repository.save(Arrays.asList(egwene, marin)); 68 | 69 | assertThat(repository.findByLastname("al'vere").size(), is(2)); 70 | 71 | assertThat(repository.findByFirstnameAndLastname("egwene", "al'vere").size(), is(1)); 72 | assertThat(repository.findByFirstnameAndLastname("egwene", "al'vere").get(0), is(egwene)); 73 | } 74 | 75 | @Test 76 | public void partialUpdate() { 77 | 78 | Person egwene = new Person(); 79 | egwene.firstname = "egwene"; 80 | egwene.lastname = "al'vere"; 81 | egwene.city = new City("new york"); 82 | 83 | Person marin = new Person(); 84 | marin.firstname = "marin"; 85 | marin.lastname = "al'vere"; 86 | 87 | repository.save(Arrays.asList(egwene, marin)); 88 | 89 | PartialUpdate partialUpdate = PartialUpdate // 90 | .newPartialUpdate(egwene.getId(), Person.class)// 91 | .del("lastname")// 92 | .set("city.name", "Tear"); 93 | 94 | kvTemplate.update(partialUpdate); 95 | 96 | Person loaded = repository.findOne(egwene.getId()); 97 | 98 | assertThat(loaded.getLastname(), is(nullValue())); 99 | assertThat(loaded.getCity().getName(), is(equalTo("Tear"))); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /redis-repositories/src/test/java/example/RedisTestConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package example; 17 | 18 | import example.RedisTestConfiguration.CustomIndexConfiguration; 19 | 20 | import java.util.Arrays; 21 | 22 | import javax.annotation.PreDestroy; 23 | 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.boot.autoconfigure.SpringBootApplication; 26 | import org.springframework.data.redis.connection.RedisConnectionFactory; 27 | import org.springframework.data.redis.core.index.IndexConfiguration; 28 | import org.springframework.data.redis.core.index.RedisIndexDefinition; 29 | import org.springframework.data.redis.core.index.SimpleIndexDefinition; 30 | import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; 31 | 32 | /** 33 | * @author Thomas Darimont 34 | * @author Oliver Gierke 35 | * @author Christoph Strobl 36 | */ 37 | @SpringBootApplication 38 | @EnableRedisRepositories(indexConfiguration = CustomIndexConfiguration.class) 39 | public class RedisTestConfiguration { 40 | 41 | @Autowired RedisConnectionFactory factory; 42 | 43 | /** 44 | * Clear database before shut down. 45 | */ 46 | @PreDestroy 47 | public void flushTestDb() { 48 | factory.getConnection().flushDb(); 49 | } 50 | 51 | static class CustomIndexConfiguration extends IndexConfiguration { 52 | 53 | @Override 54 | protected Iterable initialConfiguration() { 55 | return Arrays.asList(new SimpleIndexDefinition("persons", "lastname")); 56 | } 57 | } 58 | } 59 | --------------------------------------------------------------------------------