Skip to content

Commit 40c8442

Browse files
committed
#153 - Create example module for Query by Example with Spring Data JPA.
1 parent bb833a2 commit 40c8442

File tree

10 files changed

+296
-0
lines changed

10 files changed

+296
-0
lines changed

jpa/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<module>security</module>
2626
<module>multiple-datasources</module>
2727
<module>eclipselink</module>
28+
<module>query-by-example</module>
2829
</modules>
2930

3031
<dependencies>

jpa/query-by-example/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+

jpa/query-by-example/pom.xml

Lines changed: 14 additions & 0 deletions
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-jpa-examples</artifactId>
8+
<version>1.0.0.BUILD-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>spring-data-jpa-query-by-example</artifactId>
12+
<name>Spring Data JPA - Query-by-Example (QBE)</name>
13+
14+
</project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.springdata.jpa.querybyexample;
17+
18+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
19+
import org.springframework.boot.orm.jpa.EntityScan;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
22+
23+
/**
24+
* @author Mark Paluch
25+
*/
26+
@Configuration
27+
@EnableAutoConfiguration
28+
@EntityScan(basePackageClasses = { ApplicationConfiguration.class })
29+
@EnableJpaAuditing
30+
public class ApplicationConfiguration {
31+
32+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.springdata.jpa.querybyexample;
17+
18+
import javax.persistence.Entity;
19+
import javax.persistence.GeneratedValue;
20+
import javax.persistence.Id;
21+
22+
import lombok.Data;
23+
24+
/**
25+
* Sample user class.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
@Entity
30+
@Data
31+
public class User {
32+
33+
@Id @GeneratedValue //
34+
private Long id;
35+
private String firstname;
36+
private String lastname;
37+
private Integer age;
38+
39+
public User() {
40+
super();
41+
}
42+
43+
public User(String firstname, String lastname, Integer age) {
44+
super();
45+
this.firstname = firstname;
46+
this.lastname = lastname;
47+
this.age = age;
48+
}
49+
50+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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.springdata.jpa.querybyexample;
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<User, Long>, QueryByExampleExecutor<User> {
28+
29+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spring.datasource.separator=/;
Lines changed: 16 additions & 0 deletions
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,137 @@
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.springdata.jpa.querybyexample;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
20+
import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*;
21+
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.SpringApplicationConfiguration;
27+
import org.springframework.data.domain.Example;
28+
import org.springframework.data.domain.ExampleSpec;
29+
import org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers;
30+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
31+
import org.springframework.transaction.annotation.Transactional;
32+
33+
/**
34+
* Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories.
35+
*
36+
* @author Mark Paluch
37+
*/
38+
@RunWith(SpringJUnit4ClassRunner.class)
39+
@Transactional
40+
@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
41+
public class UserRepositoryIntegrationTests {
42+
43+
@Autowired UserRepository repository;
44+
45+
User skyler, walter, flynn, marie, hank;
46+
47+
@Before
48+
public void setUp() {
49+
repository.deleteAll();
50+
51+
this.skyler = repository.save(new User("Skyler", "White", 45));
52+
this.walter = repository.save(new User("Walter", "White", 50));
53+
this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17));
54+
this.marie = repository.save(new User("Marie", "Schrader", 38));
55+
this.hank = repository.save(new User("Hank", "Schrader", 43));
56+
}
57+
58+
/**
59+
* @see DATAJPA-218
60+
*/
61+
@Test
62+
public void countBySimpleExample() {
63+
64+
Example<User> example = Example.of(new User(null, "White", null));
65+
66+
assertThat(repository.count(example), is(3L));
67+
}
68+
69+
/**
70+
* @see DATAJPA-218
71+
*/
72+
@Test
73+
public void ignorePropertiesAndMatchByAge() {
74+
75+
Example<User> example = ExampleSpec.of(User.class). //
76+
withIgnorePaths("firstname", "lastname").//
77+
createExample(flynn);
78+
79+
assertThat(repository.findOne(example), is(flynn));
80+
}
81+
82+
/**
83+
* @see DATAJPA-218
84+
*/
85+
@Test
86+
public void substringMatching() {
87+
88+
Example<User> example = ExampleSpec.of(User.class).//
89+
withStringMatcherEnding()//
90+
.createExample(new User("er", null, null));
91+
92+
assertThat(repository.findAll(example), hasItems(skyler, walter));
93+
}
94+
95+
/**
96+
* @see DATAJPA-218
97+
*/
98+
@Test
99+
public void matchStartingStringsIgnoreCase() {
100+
101+
Example<User> example = ExampleSpec.of(User.class). //
102+
withIgnorePaths("age").//
103+
withMatcher("firstname", startsWith()).//
104+
withMatcher("lastname", ignoreCase()).//
105+
createExample(new User("Walter", "WHITE", null));
106+
107+
assertThat(repository.findAll(example), hasItems(flynn, walter));
108+
}
109+
110+
/**
111+
* @see DATAJPA-218
112+
*/
113+
@Test
114+
public void configuringMatchersUsingLambdas() {
115+
116+
Example<User> example = ExampleSpec.of(User.class).withIgnorePaths("age"). //
117+
withMatcher("firstname", matcher -> matcher.startsWith()). //
118+
withMatcher("lastname", matcher -> matcher.ignoreCase()). //
119+
createExample(new User("Walter", "WHITE", null));
120+
121+
assertThat(repository.findAll(example), hasItems(flynn, walter));
122+
}
123+
124+
/**
125+
* @see DATAJPA-218
126+
*/
127+
@Test
128+
public void valueTransformer() {
129+
130+
Example<User> example = ExampleSpec.of(User.class). //
131+
withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50))).//
132+
createExample(new User(null, "White", 99));
133+
134+
assertThat(repository.findAll(example), hasItems(walter));
135+
}
136+
137+
}

0 commit comments

Comments
 (0)