liveBook · Manning

Query by example

As explained in Java Persistence with Spring Data and Hibernate epub

Query by Example is a query-construction technique that derives predicates from an example object and matching rules instead of requiring a query language expression.

Query by Example (QBE) builds repository predicates from an example domain object rather than a query-language expression. It combines a probe, an ExampleMatcher, and an Example; the matcher defines property rules, and the Example packages both for query generation. section 4.10 section 17.4

Use QBE when:

  • Decoupling application code from the underlying data-store API. section 4.10
  • Accommodating frequent domain-object structure changes without revising existing queries. section 4.10
  • Representing static or dynamic repository constraints through reusable matching rules. section 4.10

Matching email suffixes

Create a User probe with email set to "@someotherdomain.com". Configure ExampleMatcher.matching() to ignore "level" and "active" and match email with endsWith(). Pass Example.of(user, matcher) to userRepository.findAll(example); the generated SQL uses an email LIKE predicate.

Null reference properties are ignored, but primitive properties require explicit ignore paths. Leaving defaults such as active and level included adds conditions that can change results. String properties support starting, ending, containing, and regex matching; other types use exact matching. Nested or grouped property constraints are not supported. section 17.4 section 4.10

Listing 4.22 Query By Example tests
Path: Ch04/springdatajpa2/src/test/java/com/manning/javapersistence
 /springdatajpa/QueryByExampleTest.java
 
public class QueryByExampleTest extends SpringDataJpaApplicationTests {
 
    @Test
    void testEmailWithQueryByExample() {
        User user = new User();                                            #1
        user.setEmail("@someotherdomain.com");                             #1
 
        ExampleMatcher matcher = ExampleMatcher.matching()                 #2
                .withIgnorePaths("level", "active")                        #2
                .withMatcher("email", match -> match.endsWith());          #2
 
        Example<User> example = Example.of(user, matcher);                 #3
 
        List<User> users = userRepository.findAll(example);                #4
 
        assertEquals(4, users.size());                                     #5
    }
 
    @Test
    void testUsernameWithQueryByExample() {
        User user = new User();                                            #6
        user.setUsername("J");                                             #6
 
        ExampleMatcher matcher = ExampleMatcher.matching()                 #7
                .withIgnorePaths("level", "active")                        #7
                .withStringMatcher(ExampleMatcher.StringMatcher.STARTING)  #7
                .withIgnoreCase();                                         #7
 
        Example<User> example = Example.of(user, matcher);                 #8
 
        List<User> users = userRepository.findAll(example);                #9
 
        assertEquals(3, users.size());                                     #10
    }
}
#1 Initialize a <kbd>User</kbd> instance and set up an <kbd>email</kbd> for it. This will represent the probe.
#2 Create the <kbd>ExampleMatcher</kbd> with the help of the builder pattern. Any <kbd>null</kbd> reference property will be ignored by the matcher. However, we need to explicitly ignore the <kbd>level</kbd> and <kbd>active</kbd> properties, which are primitives. If they were not ignored, they would be included in the matcher with their default values (0 for <kbd>level</kbd> and <kbd>false</kbd> for <kbd>active</kbd>) and would change the generated query. We’ll configure the matcher condition so that the <kbd>email</kbd> property will end with a given string.
#3 Create an <kbd>Example</kbd> that puts the probe and <kbd>ExampleMatcher</kbd> together and generates the query. The query will search for users that have an <kbd>email</kbd> property ending with the string defining the <kbd>email</kbd> of the probe.
#4 Execute the query to find all users matching the probe.
#5 Verify that there are four users of this kind.
#6 Initialize a <kbd>User</kbd> instance and set up a <kbd>name</kbd> for it. This will represent the second probe.
#7 Create the <kbd>ExampleMatcher</kbd> with the help of the builder pattern. Any <kbd>null</kbd> reference property will be ignored by the matcher. Again, we need to explicitly ignore the <kbd>level</kbd> and <kbd>active</kbd> properties, which are primitives. We configure the matcher condition so that the match will be made on starting strings for the configured properties (the <kbd>username</kbd> property from the probe, in our case).
#8 Create an <kbd>Example</kbd> that puts the probe and the <kbd>ExampleMatcher</kbd> together and generates the query. The query will search for users having a <kbd>username</kbd> property that starts with the string defining the <kbd>username</kbd> of the probe.
#9 Execute the query to find all users matching the probe.
#10 Verify that there are six users of this kind.

Shows a complete Spring Data JPA test that constructs a probe, configures an ExampleMatcher, creates an Example, executes the repository query, and verifies the result count. section 4.10

How Java Persistence with Spring Data and Hibernate epub covers it

How deep it goes
Defines probes, matchers, Examples, generated predicates, primitive-property handling, and matching limitations, but omits nested constraint support.
Examples
Demonstrates email-suffix matching, username matching, ignored primitive properties, generated SQL, and MongoDB users matching a last name.
What it assumes
Assumes domain objects and repositories, while teaching probe construction, ExampleMatcher configuration, and Example creation.
What you can do after
Apply it via Spring Data repositories to construct and execute static or dynamic queries from probes and matching rules.
How it connects
Connects probes and matchers with Spring Data repositories, Spring Data JPA, Spring Data MongoDB, SQL predicates, and document queries.

This explainer was generated by Manning's knowledge graph from the text of Java Persistence with Spring Data and Hibernate epub. The section links point to the parts of the book it draws on.