From 847b3de5a1db0f5616600f2595624a93d6311a0b Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 26 Feb 2016 12:05:20 +0100 Subject: [PATCH 1/4] #153 - Create example module for Query by Example with Spring Data JPA. --- jpa/pom.xml | 1 + jpa/query-by-example/README.md | 11 ++ jpa/query-by-example/pom.xml | 14 ++ .../ApplicationConfiguration.java | 32 ++++ .../springdata/jpa/querybyexample/User.java | 52 +++++++ .../jpa/querybyexample/UserRepository.java | 29 ++++ .../jpa/querybyexample/package-info.java | 6 + .../src/main/resources/application.properties | 1 + .../src/main/resources/logback.xml | 16 ++ .../UserRepositoryIntegrationTests.java | 138 ++++++++++++++++++ 10 files changed, 300 insertions(+) create mode 100644 jpa/query-by-example/README.md create mode 100644 jpa/query-by-example/pom.xml create mode 100644 jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java create mode 100644 jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java create mode 100644 jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java create mode 100644 jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java create mode 100644 jpa/query-by-example/src/main/resources/application.properties create mode 100644 jpa/query-by-example/src/main/resources/logback.xml create mode 100644 jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java diff --git a/jpa/pom.xml b/jpa/pom.xml index f5a0818ac..2451c0fa2 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -25,6 +25,7 @@ security multiple-datasources eclipselink + query-by-example diff --git a/jpa/query-by-example/README.md b/jpa/query-by-example/README.md new file mode 100644 index 000000000..c4f459201 --- /dev/null +++ b/jpa/query-by-example/README.md @@ -0,0 +1,11 @@ +# Spring Data JPA - Query-by-Example (QBE) example + +This project contains samples of Query-by-Example of Spring Data JPA. + +## Support for Query-by-Example + +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. + +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. + +This example contains a test class to illustrate Query-by-Example with a Repository in `UserRepositoryIntegrationTests`. diff --git a/jpa/query-by-example/pom.xml b/jpa/query-by-example/pom.xml new file mode 100644 index 000000000..0a2ee1437 --- /dev/null +++ b/jpa/query-by-example/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-jpa-examples + 1.0.0.BUILD-SNAPSHOT + + + spring-data-jpa-query-by-example + Spring Data JPA - Query-by-Example (QBE) + + diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java new file mode 100644 index 000000000..2f3006ffb --- /dev/null +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.querybyexample; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.orm.jpa.EntityScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + * @author Mark Paluch + */ +@Configuration +@EnableAutoConfiguration +@EntityScan(basePackageClasses = { ApplicationConfiguration.class }) +@EnableJpaAuditing +public class ApplicationConfiguration { + +} diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java new file mode 100644 index 000000000..144e496e5 --- /dev/null +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.querybyexample; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import lombok.Data; + +/** + * Sample user class. + * + * @author Mark Paluch + */ +@Entity +@Data +public class User { + + @Id @GeneratedValue // + private Long id; + private String firstname; + private String lastname; + private Integer age; + + public User() { + super(); + } + + public User(String firstname, String lastname, Integer age) { + + super(); + + this.firstname = firstname; + this.lastname = lastname; + this.age = age; + } + +} diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java new file mode 100644 index 000000000..f5decc44b --- /dev/null +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.querybyexample; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.QueryByExampleExecutor; + +/** + * Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and + * allows execution of methods accepting {@link org.springframework.data.domain.Example}. + * + * @author Mark Paluch + */ +public interface UserRepository extends CrudRepository, QueryByExampleExecutor { + +} diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java new file mode 100644 index 000000000..39edfd595 --- /dev/null +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java @@ -0,0 +1,6 @@ +/** + * Sample showing Query-by-Example related features of Spring Data JPA. + * + * @author Mark Paluch + */ +package example.springdata.jpa.querybyexample; diff --git a/jpa/query-by-example/src/main/resources/application.properties b/jpa/query-by-example/src/main/resources/application.properties new file mode 100644 index 000000000..d98d035f1 --- /dev/null +++ b/jpa/query-by-example/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.datasource.separator=/; \ No newline at end of file diff --git a/jpa/query-by-example/src/main/resources/logback.xml b/jpa/query-by-example/src/main/resources/logback.xml new file mode 100644 index 000000000..40a8f3394 --- /dev/null +++ b/jpa/query-by-example/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java new file mode 100644 index 000000000..6bb05c5f9 --- /dev/null +++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java @@ -0,0 +1,138 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.jpa.querybyexample; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleSpec; +import org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class UserRepositoryIntegrationTests { + + @Autowired UserRepository repository; + + User skyler, walter, flynn, marie, hank; + + @Before + public void setUp() { + + repository.deleteAll(); + + this.skyler = repository.save(new User("Skyler", "White", 45)); + this.walter = repository.save(new User("Walter", "White", 50)); + this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17)); + this.marie = repository.save(new User("Marie", "Schrader", 38)); + this.hank = repository.save(new User("Hank", "Schrader", 43)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void countBySimpleExample() { + + Example example = Example.of(new User(null, "White", null)); + + assertThat(repository.count(example), is(3L)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void ignorePropertiesAndMatchByAge() { + + Example example = ExampleSpec.of(User.class). // + withIgnorePaths("firstname", "lastname").// + createExample(flynn); + + assertThat(repository.findOne(example), is(flynn)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void substringMatching() { + + Example example = ExampleSpec.of(User.class).// + withStringMatcherEnding()// + .createExample(new User("er", null, null)); + + assertThat(repository.findAll(example), hasItems(skyler, walter)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void matchStartingStringsIgnoreCase() { + + Example example = ExampleSpec.of(User.class). // + withIgnorePaths("age").// + withMatcher("firstname", startsWith()).// + withMatcher("lastname", ignoreCase()).// + createExample(new User("Walter", "WHITE", null)); + + assertThat(repository.findAll(example), hasItems(flynn, walter)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void configuringMatchersUsingLambdas() { + + Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + withMatcher("firstname", matcher -> matcher.startsWith()). // + withMatcher("lastname", matcher -> matcher.ignoreCase()). // + createExample(new User("Walter", "WHITE", null)); + + assertThat(repository.findAll(example), hasItems(flynn, walter)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void valueTransformer() { + + Example example = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// + createExample(new User(null, "White", 99)); + + assertThat(repository.findAll(example), hasItems(walter)); + } + +} From b3d99498104d75531793b2b52e91d1dbd2f51610 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 26 Feb 2016 13:31:02 +0100 Subject: [PATCH 2/4] #153 - Create example module for Query by Example with Spring Data MongoDB. --- mongodb/pom.xml | 1 + mongodb/query-by-example/README.md | 12 ++ mongodb/query-by-example/pom.xml | 14 ++ .../ApplicationConfiguration.java | 27 ++++ .../mongodb/querybyexample/User.java | 43 +++++ .../querybyexample/UserRepository.java | 30 ++++ .../mongodb/querybyexample/package-info.java | 22 +++ .../src/main/resources/logback.xml | 16 ++ .../MongoOperationsIntegrationTests.java | 143 +++++++++++++++++ .../UserRepositoryIntegrationTests.java | 149 ++++++++++++++++++ 10 files changed, 457 insertions(+) create mode 100644 mongodb/query-by-example/README.md create mode 100644 mongodb/query-by-example/pom.xml create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java create mode 100644 mongodb/query-by-example/src/main/resources/logback.xml create mode 100644 mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java create mode 100644 mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java diff --git a/mongodb/pom.xml b/mongodb/pom.xml index 0c2854e2d..35e2bab4a 100644 --- a/mongodb/pom.xml +++ b/mongodb/pom.xml @@ -23,6 +23,7 @@ java8 security geo-json + query-by-example diff --git a/mongodb/query-by-example/README.md b/mongodb/query-by-example/README.md new file mode 100644 index 000000000..01ad499cb --- /dev/null +++ b/mongodb/query-by-example/README.md @@ -0,0 +1,12 @@ +# Spring Data MongoDB - Query-by-Example (QBE) example + +This project contains samples of Query-by-Example of Spring Data MongoDB. + +## Support for Query-by-Example + +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. + +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. + +This example contains two test classes to illustrate Query-by-Example with `MongoOperations` in `MongoOperationsIntegrationTests` and the usage with a Repository in `UserRepositoryIntegrationTests`. + diff --git a/mongodb/query-by-example/pom.xml b/mongodb/query-by-example/pom.xml new file mode 100644 index 000000000..a0652f4cb --- /dev/null +++ b/mongodb/query-by-example/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + + + org.springframework.data.examples + spring-data-mongodb-examples + 1.0.0.BUILD-SNAPSHOT + + + spring-data-mongodb-query-by-example + Spring Data MongoDB - Query-by-Example (QBE) + + diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java new file mode 100644 index 000000000..3b121ff68 --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Mark Paluch + */ +@SpringBootApplication +public class ApplicationConfiguration { + +} diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java new file mode 100644 index 000000000..936b82a26 --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; + +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * Sample user class. + * + * @author Mark Paluch + */ +@Data +@RequiredArgsConstructor +@Document +public class User { + + @Id // + private ObjectId id; + private final String firstname; + private final String lastname; + private final Integer age; + +} diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java new file mode 100644 index 000000000..14241543c --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.QueryByExampleExecutor; + +/** + * Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and + * allows execution of methods accepting {@link org.springframework.data.domain.Example}. + * + * @author Mark Paluch + */ +public interface UserRepository extends CrudRepository, QueryByExampleExecutor { + +} diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java new file mode 100644 index 000000000..3b8fa0cc2 --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Sample showing Query-by-Example related features of Spring Data MongoDB. + * + * @author Mark Paluch + */ +package example.springdata.mongodb.querybyexample; diff --git a/mongodb/query-by-example/src/main/resources/logback.xml b/mongodb/query-by-example/src/main/resources/logback.xml new file mode 100644 index 000000000..40a8f3394 --- /dev/null +++ b/mongodb/query-by-example/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + \ No newline at end of file diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java new file mode 100644 index 000000000..c323986f1 --- /dev/null +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java @@ -0,0 +1,143 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleSpec; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class MongoOperationsIntegrationTests { + + @Autowired MongoOperations operations; + + User skyler, walter, flynn, marie, hank; + + @Before + public void setUp() { + + operations.remove(new Query(), User.class); + + this.skyler = new User("Skyler", "White", 45); + this.walter = new User("Walter", "White", 50); + this.flynn = new User("Walter Jr. (Flynn)", "White", 17); + this.marie = new User("Marie", "Schrader", 38); + this.hank = new User("Hank", "Schrader", 43); + + operations.save(this.skyler); + operations.save(this.walter); + operations.save(this.flynn); + operations.save(this.marie); + operations.save(this.hank); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void ignoreNullProperties() { + + assertThat(operations.findByExample(new User(null, null, 17)), hasItems(flynn)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void substringMatching() { + + Example example = ExampleSpec.of(User.class).// + withStringMatcherEnding().// + createExample(new User("er", null, null)); + + assertThat(operations.findByExample(example), hasItems(skyler, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void regexMatching() { + + Example example = ExampleSpec.of(User.class).// + withMatcher("firstname", matcher -> matcher.regex()).// + createExample(new User("(Skyl|Walt)er", null, null)); + + assertThat(operations.findByExample(example), hasItems(skyler, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void matchStartingStringsIgnoreCase() { + + Example example = ExampleSpec.of(User.class). // + withIgnorePaths("age").// + withMatcher("firstname", startsWith()).// + withMatcher("lastname", ignoreCase()).// + createExample(new User("Walter", "WHITE", null)); + + assertThat(operations.findByExample(example), hasItems(flynn, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void configuringMatchersUsingLambdas() { + + Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + withMatcher("firstname", matcher -> matcher.startsWith()). // + withMatcher("lastname", matcher -> matcher.ignoreCase()). // + createExample(new User("Walter", "WHITE", null)); + + assertThat(operations.findByExample(example), hasItems(flynn, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void valueTransformer() { + + Example example = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// + createExample(new User(null, "White", 99)); + + assertThat(operations.findByExample(example), hasItems(walter)); + } + +} diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java new file mode 100644 index 000000000..3a77f3132 --- /dev/null +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java @@ -0,0 +1,149 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleSpec; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class UserRepositoryIntegrationTests { + + @Autowired UserRepository repository; + + User skyler, walter, flynn, marie, hank; + + @Before + public void setUp() { + + repository.deleteAll(); + + this.skyler = repository.save(new User("Skyler", "White", 45)); + this.walter = repository.save(new User("Walter", "White", 50)); + this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17)); + this.marie = repository.save(new User("Marie", "Schrader", 38)); + this.hank = repository.save(new User("Hank", "Schrader", 43)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void countBySimpleExample() { + + Example example = Example.of(new User(null, "White", null)); + + assertThat(repository.count(example), is(3L)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void ignorePropertiesAndMatchByAge() { + + Example example = ExampleSpec.of(User.class). // + withIgnorePaths("firstname", "lastname").// + createExample(flynn); + + assertThat(repository.findOne(example), is(flynn)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void substringMatching() { + + Example example = ExampleSpec.of(User.class).// + withStringMatcherEnding()// + .createExample(new User("er", null, null)); + + assertThat(repository.findAll(example), hasItems(skyler, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void regexMatching() { + + Example example = ExampleSpec.of(User.class).// + withMatcher("firstname", matcher -> matcher.regex()).// + createExample(new User("(Skyl|Walt)er", null, null)); + + assertThat(repository.findAll(example), hasItems(skyler, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void matchStartingStringsIgnoreCase() { + + Example example = ExampleSpec.of(User.class). // + withIgnorePaths("age").// + withMatcher("firstname", startsWith()).// + withMatcher("lastname", ignoreCase()).// + createExample(new User("Walter", "WHITE", null)); + + assertThat(repository.findAll(example), hasItems(flynn, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void configuringMatchersUsingLambdas() { + + Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + withMatcher("firstname", matcher -> matcher.startsWith()). // + withMatcher("lastname", matcher -> matcher.ignoreCase()). // + createExample(new User("Walter", "WHITE", null)); + + assertThat(repository.findAll(example), hasItems(flynn, walter)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void valueTransformer() { + + Example example = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// + createExample(new User(null, "White", 99)); + + assertThat(repository.findAll(example), hasItems(walter)); + } + +} From bda726be7c049525b9fd99b8d22c822ee40fa498 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 29 Feb 2016 12:14:08 +0100 Subject: [PATCH 3/4] #153 - Update Example with changes from reviews. --- .../springdata/jpa/querybyexample/User.java | 6 +-- .../UserRepositoryIntegrationTests.java | 37 +++++++-------- .../MongoOperationsIntegrationTests.java | 38 ++++++++------- .../UserRepositoryIntegrationTests.java | 46 +++++++++---------- 4 files changed, 57 insertions(+), 70 deletions(-) diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java index 144e496e5..fc5d7a1ac 100644 --- a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java @@ -20,6 +20,7 @@ import javax.persistence.Id; import lombok.Data; +import lombok.NoArgsConstructor; /** * Sample user class. @@ -28,6 +29,7 @@ */ @Entity @Data +@NoArgsConstructor public class User { @Id @GeneratedValue // @@ -36,10 +38,6 @@ public class User { private String lastname; private Integer age; - public User() { - super(); - } - public User(String firstname, String lastname, Integer age) { super(); diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java index 6bb05c5f9..b4253ceef 100644 --- a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java +++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java @@ -18,6 +18,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith; import org.junit.Before; import org.junit.Test; @@ -26,7 +27,6 @@ import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleSpec; -import org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -73,11 +73,10 @@ public void countBySimpleExample() { @Test public void ignorePropertiesAndMatchByAge() { - Example example = ExampleSpec.of(User.class). // - withIgnorePaths("firstname", "lastname").// - createExample(flynn); + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + withIgnorePaths("firstname", "lastname"); - assertThat(repository.findOne(example), is(flynn)); + assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn)); } /** @@ -86,11 +85,10 @@ public void ignorePropertiesAndMatchByAge() { @Test public void substringMatching() { - Example example = ExampleSpec.of(User.class).// - withStringMatcherEnding()// - .createExample(new User("er", null, null)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + withStringMatcherEnding(); - assertThat(repository.findAll(example), hasItems(skyler, walter)); + assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); } /** @@ -99,13 +97,12 @@ public void substringMatching() { @Test public void matchStartingStringsIgnoreCase() { - Example example = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // withIgnorePaths("age").// withMatcher("firstname", startsWith()).// - withMatcher("lastname", ignoreCase()).// - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", ignoreCase()); - assertThat(repository.findAll(example), hasItems(flynn, walter)); + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); } /** @@ -114,12 +111,11 @@ public void matchStartingStringsIgnoreCase() { @Test public void configuringMatchersUsingLambdas() { - Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // withMatcher("firstname", matcher -> matcher.startsWith()). // - withMatcher("lastname", matcher -> matcher.ignoreCase()). // - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", matcher -> matcher.ignoreCase()); - assertThat(repository.findAll(example), hasItems(flynn, walter)); + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); } /** @@ -128,11 +124,10 @@ public void configuringMatchersUsingLambdas() { @Test public void valueTransformer() { - Example example = ExampleSpec.of(User.class). // - withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// - createExample(new User(null, "White", 99)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); - assertThat(repository.findAll(example), hasItems(walter)); + assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); } } diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java index c323986f1..2bfdf05cc 100644 --- a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java @@ -78,11 +78,10 @@ public void ignoreNullProperties() { @Test public void substringMatching() { - Example example = ExampleSpec.of(User.class).// - withStringMatcherEnding().// - createExample(new User("er", null, null)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + withStringMatcherEnding(); - assertThat(operations.findByExample(example), hasItems(skyler, walter)); + assertThat(operations.findByExample(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); } /** @@ -91,11 +90,11 @@ public void substringMatching() { @Test public void regexMatching() { - Example example = ExampleSpec.of(User.class).// - withMatcher("firstname", matcher -> matcher.regex()).// - createExample(new User("(Skyl|Walt)er", null, null)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + withMatcher("firstname", matcher -> matcher.regex()); - assertThat(operations.findByExample(example), hasItems(skyler, walter)); + assertThat(operations.findByExample(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)), + hasItems(skyler, walter)); } /** @@ -104,13 +103,13 @@ public void regexMatching() { @Test public void matchStartingStringsIgnoreCase() { - Example example = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // withIgnorePaths("age").// withMatcher("firstname", startsWith()).// - withMatcher("lastname", ignoreCase()).// - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", ignoreCase()); - assertThat(operations.findByExample(example), hasItems(flynn, walter)); + assertThat(operations.findByExample(Example.of(new User("Walter", "WHITE", null), exampleSpec)), + hasItems(flynn, walter)); } /** @@ -119,12 +118,12 @@ public void matchStartingStringsIgnoreCase() { @Test public void configuringMatchersUsingLambdas() { - Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // withMatcher("firstname", matcher -> matcher.startsWith()). // - withMatcher("lastname", matcher -> matcher.ignoreCase()). // - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", matcher -> matcher.ignoreCase()); - assertThat(operations.findByExample(example), hasItems(flynn, walter)); + assertThat(operations.findByExample(Example.of(new User("Walter", "WHITE", null), exampleSpec)), + hasItems(flynn, walter)); } /** @@ -133,11 +132,10 @@ public void configuringMatchersUsingLambdas() { @Test public void valueTransformer() { - Example example = ExampleSpec.of(User.class). // - withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// - createExample(new User(null, "White", 99)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); - assertThat(operations.findByExample(example), hasItems(walter)); + assertThat(operations.findByExample(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); } } diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java index 3a77f3132..5f9457bf7 100644 --- a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java @@ -18,7 +18,8 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.ignoreCase; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith; import org.junit.Before; import org.junit.Test; @@ -71,11 +72,10 @@ public void countBySimpleExample() { @Test public void ignorePropertiesAndMatchByAge() { - Example example = ExampleSpec.of(User.class). // - withIgnorePaths("firstname", "lastname").// - createExample(flynn); + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + withIgnorePaths("firstname", "lastname"); - assertThat(repository.findOne(example), is(flynn)); + assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn)); } /** @@ -84,11 +84,10 @@ public void ignorePropertiesAndMatchByAge() { @Test public void substringMatching() { - Example example = ExampleSpec.of(User.class).// - withStringMatcherEnding()// - .createExample(new User("er", null, null)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + withStringMatcherEnding(); - assertThat(repository.findAll(example), hasItems(skyler, walter)); + assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); } /** @@ -97,11 +96,11 @@ public void substringMatching() { @Test public void regexMatching() { - Example example = ExampleSpec.of(User.class).// - withMatcher("firstname", matcher -> matcher.regex()).// - createExample(new User("(Skyl|Walt)er", null, null)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + withMatcher("firstname", matcher -> matcher.regex()); - assertThat(repository.findAll(example), hasItems(skyler, walter)); + assertThat(repository.findAll(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)), + hasItems(skyler, walter)); } /** @@ -110,13 +109,12 @@ public void regexMatching() { @Test public void matchStartingStringsIgnoreCase() { - Example example = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // withIgnorePaths("age").// withMatcher("firstname", startsWith()).// - withMatcher("lastname", ignoreCase()).// - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", ignoreCase()); - assertThat(repository.findAll(example), hasItems(flynn, walter)); + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); } /** @@ -125,12 +123,11 @@ public void matchStartingStringsIgnoreCase() { @Test public void configuringMatchersUsingLambdas() { - Example example = ExampleSpec.of(User.class).withIgnorePaths("age"). // + ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // withMatcher("firstname", matcher -> matcher.startsWith()). // - withMatcher("lastname", matcher -> matcher.ignoreCase()). // - createExample(new User("Walter", "WHITE", null)); + withMatcher("lastname", matcher -> matcher.ignoreCase()); - assertThat(repository.findAll(example), hasItems(flynn, walter)); + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); } /** @@ -139,11 +136,10 @@ public void configuringMatchersUsingLambdas() { @Test public void valueTransformer() { - Example example = ExampleSpec.of(User.class). // - withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).// - createExample(new User(null, "White", 99)); + ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); - assertThat(repository.findAll(example), hasItems(walter)); + assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); } } From 1d17bc25ce41ae33f33657466f02b12c8bc26f13 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 1 Mar 2016 12:28:13 +0100 Subject: [PATCH 4/4] #153 - Add typed and untyped Examples. --- .../jpa/querybyexample/SpecialUser.java | 37 +++++ ...RepositoryInheritanceIntegrationTests.java | 93 ++++++++++++ .../UserRepositoryIntegrationTests.java | 135 +++++++++--------- .../mongodb/querybyexample/Contact.java | 42 ++++++ .../querybyexample/ContactRepository.java | 30 ++++ .../mongodb/querybyexample/User.java | 2 +- .../ContactRepositoryIntegrationTests.java | 114 +++++++++++++++ .../MongoOperationsIntegrationTests.java | 11 +- .../UserRepositoryIntegrationTests.java | 12 +- 9 files changed, 397 insertions(+), 79 deletions(-) create mode 100644 jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java create mode 100644 jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java create mode 100644 mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java create mode 100644 mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java new file mode 100644 index 000000000..5b46915bb --- /dev/null +++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.jpa.querybyexample; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; + +/** + * Sample class that extends {@link User}. + * + * @author Mark Paluch + */ +@Entity +@Data +@NoArgsConstructor +public class SpecialUser extends User { + + public SpecialUser(String firstname, String lastname, Integer age) { + super(firstname, lastname, age); + } +} diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java new file mode 100644 index 000000000..1d8f2cf41 --- /dev/null +++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.jpa.querybyexample; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*; +import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleSpec; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories. + * + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class UserRepositoryInheritanceIntegrationTests { + + @Autowired UserRepository repository; + + User skyler, walter, flynn; + + @Before + public void setUp() { + + repository.deleteAll(); + + this.skyler = repository.save(new User("Skyler", "White", 45)); + this.walter = repository.save(new SpecialUser("Walter", "White", 50)); + this.flynn = repository.save(new SpecialUser("Walter Jr. (Flynn)", "White", 17)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void countBySimpleExample() { + + Example example = Example.of(new SpecialUser(null, "White", null)); + + assertThat(repository.count(example), is(3L)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void countUserByTypedExample() { + + Example example = Example.of(new SpecialUser(null, "White", null), // + ExampleSpec.typed(User.class)); + + assertThat(repository.count(example), is(3L)); + } + + /** + * @see DATAJPA-218 + */ + @Test + public void countSpecialUserByTypedExample() { + + Example example = Example.of(new SpecialUser(null, "White", null), // + ExampleSpec.typed(SpecialUser.class)); + + assertThat(repository.count(example), is(2L)); + } + +} diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java index b4253ceef..4a784f2b2 100644 --- a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java +++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java @@ -40,94 +40,95 @@ @SpringApplicationConfiguration(classes = ApplicationConfiguration.class) public class UserRepositoryIntegrationTests { - @Autowired UserRepository repository; + @Autowired + UserRepository repository; - User skyler, walter, flynn, marie, hank; + User skyler, walter, flynn, marie, hank; - @Before - public void setUp() { + @Before + public void setUp() { - repository.deleteAll(); + repository.deleteAll(); - this.skyler = repository.save(new User("Skyler", "White", 45)); - this.walter = repository.save(new User("Walter", "White", 50)); - this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17)); - this.marie = repository.save(new User("Marie", "Schrader", 38)); - this.hank = repository.save(new User("Hank", "Schrader", 43)); - } + this.skyler = repository.save(new User("Skyler", "White", 45)); + this.walter = repository.save(new User("Walter", "White", 50)); + this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17)); + this.marie = repository.save(new User("Marie", "Schrader", 38)); + this.hank = repository.save(new User("Hank", "Schrader", 43)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void countBySimpleExample() { + /** + * @see DATAJPA-218 + */ + @Test + public void countBySimpleExample() { - Example example = Example.of(new User(null, "White", null)); + Example example = Example.of(new User(null, "White", null)); - assertThat(repository.count(example), is(3L)); - } + assertThat(repository.count(example), is(3L)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void ignorePropertiesAndMatchByAge() { + /** + * @see DATAJPA-218 + */ + @Test + public void ignorePropertiesAndMatchByAge() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // - withIgnorePaths("firstname", "lastname"); + ExampleSpec exampleSpec = ExampleSpec.untyped(). // + withIgnorePaths("firstname", "lastname"); - assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn)); - } + assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void substringMatching() { + /** + * @see DATAJPA-218 + */ + @Test + public void substringMatching() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).// - withStringMatcherEnding(); + ExampleSpec exampleSpec = ExampleSpec.untyped().// + withStringMatcherEnding(); - assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); - } + assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void matchStartingStringsIgnoreCase() { + /** + * @see DATAJPA-218 + */ + @Test + public void matchStartingStringsIgnoreCase() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // - withIgnorePaths("age").// - withMatcher("firstname", startsWith()).// - withMatcher("lastname", ignoreCase()); + ExampleSpec exampleSpec = ExampleSpec.untyped(). // + withIgnorePaths("age").// + withMatcher("firstname", startsWith()).// + withMatcher("lastname", ignoreCase()); - assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); - } + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void configuringMatchersUsingLambdas() { + /** + * @see DATAJPA-218 + */ + @Test + public void configuringMatchersUsingLambdas() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // - withMatcher("firstname", matcher -> matcher.startsWith()). // - withMatcher("lastname", matcher -> matcher.ignoreCase()); + ExampleSpec exampleSpec = ExampleSpec.untyped().withIgnorePaths("age"). // + withMatcher("firstname", matcher -> matcher.startsWith()). // + withMatcher("lastname", matcher -> matcher.ignoreCase()); - assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); - } + assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter)); + } - /** - * @see DATAJPA-218 - */ - @Test - public void valueTransformer() { + /** + * @see DATAJPA-218 + */ + @Test + public void valueTransformer() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // - withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); + ExampleSpec exampleSpec = ExampleSpec.untyped(). // + withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); - assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); - } + assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); + } } diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java new file mode 100644 index 000000000..90d4651ed --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +/** + * Sample contact class. + * + * @author Mark Paluch + */ +@Data +@RequiredArgsConstructor +@Document(collection = "collectionStoringTwoTypes") +public class Contact { + + @Id // + private ObjectId id; + private final String firstname; + private final String lastname; + private final Integer age; + +} diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java new file mode 100644 index 000000000..6c7083e54 --- /dev/null +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.QueryByExampleExecutor; + +/** + * Simple repository interface for {@link Contact} instances. The interface implements {@link QueryByExampleExecutor} and + * allows execution of methods accepting {@link org.springframework.data.domain.Example}. + * + * @author Mark Paluch + */ +public interface ContactRepository extends CrudRepository, QueryByExampleExecutor { + +} diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java index 936b82a26..56b84fe74 100644 --- a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java +++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java @@ -31,7 +31,7 @@ */ @Data @RequiredArgsConstructor -@Document +@Document(collection = "collectionStoringTwoTypes") public class User { @Id // diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java new file mode 100644 index 000000000..b3a87af6a --- /dev/null +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.springdata.mongodb.querybyexample; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleSpec; +import org.springframework.data.domain.ExampleSpec.StringMatcher; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories for a case where two domain types are stored in one collection. + * + * @author Mark Paluch + * @soundtrack Paul van Dyk - VONYC Sessions Episode 496 with guest Armin van Buuren + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ApplicationConfiguration.class) +public class ContactRepositoryIntegrationTests { + + @Autowired UserRepository userRepository; + @Autowired ContactRepository contactRepository; + @Autowired MongoOperations mongoOperations; + + User skyler, walter, flynn; + Contact marie, hank; + + @Before + public void setUp() { + + userRepository.deleteAll(); + + this.skyler = userRepository.save(new User("Skyler", "White", 45)); + this.walter = userRepository.save(new User("Walter", "White", 50)); + this.flynn = userRepository.save(new User("Walter Jr. (Flynn)", "White", 17)); + this.marie = contactRepository.save(new Contact("Marie", "Schrader", 38)); + this.hank = contactRepository.save(new Contact("Hank", "Schrader", 43)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void countUsersByUntypedExample() { + + Example example = Example.of(new User(null, null, null)); + + // the count is 5 because untyped examples do not restrict the type + assertThat(userRepository.count(example), is(5L)); + assertThat(mongoOperations.count(new Query(), "collectionStoringTwoTypes"), is(5L)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void countByTypedExample() { + + Example example = Example.of(new User(null, null, null), ExampleSpec.typed(User.class)); + + assertThat(userRepository.count(example), is(3L)); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void findAllUsersBySimpleExample() { + + Example example = Example.of(new User(".*", null, null), // + ExampleSpec.typed(User.class).withStringMatcher(StringMatcher.REGEX)); + + assertThat(userRepository.findAll(example), containsInAnyOrder(skyler, walter, flynn)); + assertThat(userRepository.findAll(example), not(containsInAnyOrder(hank, marie))); + } + + /** + * @see DATAMONGO-1245 + */ + @Test + public void findAllContactsBySimpleExample() { + + Example example = Example.of(new Contact(".*", null, null), // + ExampleSpec.typed(Contact.class).withStringMatcher(StringMatcher.REGEX)); + + assertThat(contactRepository.findAll(example), containsInAnyOrder(hank, marie)); + assertThat(contactRepository.findAll(example), not(containsInAnyOrder(skyler, walter, flynn))); + } + +} diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java index 2bfdf05cc..fb2a2bded 100644 --- a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleSpec; +import org.springframework.data.domain.TypedExampleSpec; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.Query; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -78,7 +79,7 @@ public void ignoreNullProperties() { @Test public void substringMatching() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).// withStringMatcherEnding(); assertThat(operations.findByExample(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); @@ -90,7 +91,7 @@ public void substringMatching() { @Test public void regexMatching() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).// withMatcher("firstname", matcher -> matcher.regex()); assertThat(operations.findByExample(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)), @@ -103,7 +104,7 @@ public void regexMatching() { @Test public void matchStartingStringsIgnoreCase() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class). // withIgnorePaths("age").// withMatcher("firstname", startsWith()).// withMatcher("lastname", ignoreCase()); @@ -118,7 +119,7 @@ public void matchStartingStringsIgnoreCase() { @Test public void configuringMatchersUsingLambdas() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // + TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).withIgnorePaths("age"). // withMatcher("firstname", matcher -> matcher.startsWith()). // withMatcher("lastname", matcher -> matcher.ignoreCase()); @@ -132,7 +133,7 @@ public void configuringMatchersUsingLambdas() { @Test public void valueTransformer() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class). // withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); assertThat(operations.findByExample(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter)); diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java index 5f9457bf7..575fb2b27 100644 --- a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java +++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java @@ -72,7 +72,7 @@ public void countBySimpleExample() { @Test public void ignorePropertiesAndMatchByAge() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.untyped(). // withIgnorePaths("firstname", "lastname"); assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn)); @@ -84,7 +84,7 @@ public void ignorePropertiesAndMatchByAge() { @Test public void substringMatching() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + ExampleSpec exampleSpec = ExampleSpec.untyped().// withStringMatcherEnding(); assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter)); @@ -96,7 +96,7 @@ public void substringMatching() { @Test public void regexMatching() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).// + ExampleSpec exampleSpec = ExampleSpec.untyped().// withMatcher("firstname", matcher -> matcher.regex()); assertThat(repository.findAll(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)), @@ -109,7 +109,7 @@ public void regexMatching() { @Test public void matchStartingStringsIgnoreCase() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.untyped(). // withIgnorePaths("age").// withMatcher("firstname", startsWith()).// withMatcher("lastname", ignoreCase()); @@ -123,7 +123,7 @@ public void matchStartingStringsIgnoreCase() { @Test public void configuringMatchersUsingLambdas() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class).withIgnorePaths("age"). // + ExampleSpec exampleSpec = ExampleSpec.untyped().withIgnorePaths("age"). // withMatcher("firstname", matcher -> matcher.startsWith()). // withMatcher("lastname", matcher -> matcher.ignoreCase()); @@ -136,7 +136,7 @@ public void configuringMatchersUsingLambdas() { @Test public void valueTransformer() { - ExampleSpec exampleSpec = ExampleSpec.of(User.class). // + ExampleSpec exampleSpec = ExampleSpec.untyped(). // withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))); assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter));