Skip to content

Commit b3d9949

Browse files
committed
#153 - Create example module for Query by Example with Spring Data MongoDB.
1 parent 847b3de commit b3d9949

File tree

10 files changed

+457
-0
lines changed

10 files changed

+457
-0
lines changed

mongodb/pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<module>java8</module>
2424
<module>security</module>
2525
<module>geo-json</module>
26+
<module>query-by-example</module>
2627
</modules>
2728

2829
<dependencies>

mongodb/query-by-example/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
This example contains two test classes to illustrate Query-by-Example with `MongoOperations` in `MongoOperationsIntegrationTests` and the usage with a Repository in `UserRepositoryIntegrationTests`.
12+

mongodb/query-by-example/pom.xml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>org.springframework.data.examples</groupId>
7+
<artifactId>spring-data-mongodb-examples</artifactId>
8+
<version>1.0.0.BUILD-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>spring-data-mongodb-query-by-example</artifactId>
12+
<name>Spring Data MongoDB - Query-by-Example (QBE)</name>
13+
14+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.springdata.mongodb.querybyexample;
18+
19+
import org.springframework.boot.autoconfigure.SpringBootApplication;
20+
21+
/**
22+
* @author Mark Paluch
23+
*/
24+
@SpringBootApplication
25+
public class ApplicationConfiguration {
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.springdata.mongodb.querybyexample;
18+
19+
import lombok.Data;
20+
import lombok.NoArgsConstructor;
21+
import lombok.RequiredArgsConstructor;
22+
23+
import org.bson.types.ObjectId;
24+
import org.springframework.data.annotation.Id;
25+
import org.springframework.data.mongodb.core.mapping.Document;
26+
27+
/**
28+
* Sample user class.
29+
*
30+
* @author Mark Paluch
31+
*/
32+
@Data
33+
@RequiredArgsConstructor
34+
@Document
35+
public class User {
36+
37+
@Id //
38+
private ObjectId id;
39+
private final String firstname;
40+
private final String lastname;
41+
private final Integer age;
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.springdata.mongodb.querybyexample;
18+
19+
import org.springframework.data.repository.CrudRepository;
20+
import org.springframework.data.repository.query.QueryByExampleExecutor;
21+
22+
/**
23+
* Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and
24+
* allows execution of methods accepting {@link org.springframework.data.domain.Example}.
25+
*
26+
* @author Mark Paluch
27+
*/
28+
public interface UserRepository extends CrudRepository<User, Long>, QueryByExampleExecutor<User> {
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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.springdata.mongodb.querybyexample;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
4+
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
5+
<encoder>
6+
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
7+
</encoder>
8+
</appender>
9+
10+
<logger name="org.springframework" level="error" />
11+
12+
<root level="error">
13+
<appender-ref ref="console" />
14+
</root>
15+
16+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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.springdata.mongodb.querybyexample;
18+
19+
import static org.hamcrest.CoreMatchers.*;
20+
import static org.junit.Assert.*;
21+
import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*;
22+
import static org.springframework.data.domain.ExampleSpec.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.SpringApplicationConfiguration;
29+
import org.springframework.data.domain.Example;
30+
import org.springframework.data.domain.ExampleSpec;
31+
import org.springframework.data.mongodb.core.MongoOperations;
32+
import org.springframework.data.mongodb.core.query.Query;
33+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
34+
35+
/**
36+
* Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories.
37+
*
38+
* @author Mark Paluch
39+
*/
40+
@RunWith(SpringJUnit4ClassRunner.class)
41+
@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
42+
public class MongoOperationsIntegrationTests {
43+
44+
@Autowired MongoOperations operations;
45+
46+
User skyler, walter, flynn, marie, hank;
47+
48+
@Before
49+
public void setUp() {
50+
51+
operations.remove(new Query(), User.class);
52+
53+
this.skyler = new User("Skyler", "White", 45);
54+
this.walter = new User("Walter", "White", 50);
55+
this.flynn = new User("Walter Jr. (Flynn)", "White", 17);
56+
this.marie = new User("Marie", "Schrader", 38);
57+
this.hank = new User("Hank", "Schrader", 43);
58+
59+
operations.save(this.skyler);
60+
operations.save(this.walter);
61+
operations.save(this.flynn);
62+
operations.save(this.marie);
63+
operations.save(this.hank);
64+
}
65+
66+
/**
67+
* @see DATAMONGO-1245
68+
*/
69+
@Test
70+
public void ignoreNullProperties() {
71+
72+
assertThat(operations.findByExample(new User(null, null, 17)), hasItems(flynn));
73+
}
74+
75+
/**
76+
* @see DATAMONGO-1245
77+
*/
78+
@Test
79+
public void substringMatching() {
80+
81+
Example<User> example = ExampleSpec.of(User.class).//
82+
withStringMatcherEnding().//
83+
createExample(new User("er", null, null));
84+
85+
assertThat(operations.findByExample(example), hasItems(skyler, walter));
86+
}
87+
88+
/**
89+
* @see DATAMONGO-1245
90+
*/
91+
@Test
92+
public void regexMatching() {
93+
94+
Example<User> example = ExampleSpec.of(User.class).//
95+
withMatcher("firstname", matcher -> matcher.regex()).//
96+
createExample(new User("(Skyl|Walt)er", null, null));
97+
98+
assertThat(operations.findByExample(example), hasItems(skyler, walter));
99+
}
100+
101+
/**
102+
* @see DATAMONGO-1245
103+
*/
104+
@Test
105+
public void matchStartingStringsIgnoreCase() {
106+
107+
Example<User> example = ExampleSpec.of(User.class). //
108+
withIgnorePaths("age").//
109+
withMatcher("firstname", startsWith()).//
110+
withMatcher("lastname", ignoreCase()).//
111+
createExample(new User("Walter", "WHITE", null));
112+
113+
assertThat(operations.findByExample(example), hasItems(flynn, walter));
114+
}
115+
116+
/**
117+
* @see DATAMONGO-1245
118+
*/
119+
@Test
120+
public void configuringMatchersUsingLambdas() {
121+
122+
Example<User> example = ExampleSpec.of(User.class).withIgnorePaths("age"). //
123+
withMatcher("firstname", matcher -> matcher.startsWith()). //
124+
withMatcher("lastname", matcher -> matcher.ignoreCase()). //
125+
createExample(new User("Walter", "WHITE", null));
126+
127+
assertThat(operations.findByExample(example), hasItems(flynn, walter));
128+
}
129+
130+
/**
131+
* @see DATAMONGO-1245
132+
*/
133+
@Test
134+
public void valueTransformer() {
135+
136+
Example<User> example = ExampleSpec.of(User.class). //
137+
withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).//
138+
createExample(new User(null, "White", 99));
139+
140+
assertThat(operations.findByExample(example), hasItems(walter));
141+
}
142+
143+
}

0 commit comments

Comments
 (0)