An open API service indexing awesome lists of open source software.

https://github.com/jaxio/jpa-query-by-example

The JPA Query by Example framework is used by projects generated by Celerio.
https://github.com/jaxio/jpa-query-by-example

Last synced: 2 months ago
JSON representation

The JPA Query by Example framework is used by projects generated by Celerio.

Awesome Lists containing this project

README

          

## JPA Query by Example framework

[![Build Status](https://travis-ci.org/jaxio/jpa-query-by-example.svg?branch=master)](https://travis-ci.org/jaxio/jpa-query-by-example)

Query By Example for JPA is originally inspired from [Hibernate Example criterion](https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querycriteria.html#querycriteria-examples
). But since Hibernate's Example is not part of JPA 2, we have created our own API, using JPA 2 only.

## How To Use QBE

We do not cover here QBE implementation details, instead we explain how to use the Query By Example API.

JPA Query by Example is available on Maven central repository:

```xml

com.jaxio
jpa-querybyexample
1.0.1

```

#### Resources

* Take a look directly at the [QBE junit tests](https://github.com/jaxio/jpa-query-by-example/blob/master/src/test/java/demo), they are almost self-explanatory.
* Use Celerio to generate an advanced CRUD application that leverages this QBE API. See [Celerio](http://www.jaxio.com/documentation/celerio/installation.html)
* [Watch a demo of an application generated by Celerio](https://www.facebook.com/video/video.php?v=524162864265905&notif_t=video_processed)

### Simple Query By Example

In its simplest form, Query By Example allows you to construct a query from a given entity instance.

Let's assume we have an [Account entity](https://github.com/jaxio/jpa-query-by-example/blob/master/src/test/java/demo/Account.java)
having a `lastName` property and that we want to query all accounts whose last name matches 'Jagger'.

Using QBE, constructing the query is as simple as setting the lastName...:

```java
Account example = new Account();
example.setLastName("Jagger");
List result = accountRepository.find(example);
```

At the SQL level, the resulting query looks like this:

```sql
select
-- skip other fields for clarity
account0_.LAST_NAME as LAST9_3_,
from
Account account0_
where
account0_.LAST_NAME=?
```

The [AccountRepository](https://github.com/jaxio/jpa-query-by-example/blob/master/src/test/java/demo/AccountRepository.java)
extends a [GenericRepository](https://github.com/jaxio/jpa-query-by-example/blob/master/src/main/java/com/jaxio/jpa/querybyexample/GenericRepository.java)

#### Case sensitivity, order by

The first query above involves a String. Let's change it to make it case insensitive.

Our `Account` entity does not carry case sensitivity meta information. For this reason, we require some extra parameters
for case sensitivity, but also ordering, etc.
The number of parameters can grow quickly, so we have grouped them in the
[SearchParameters](https://github.com/jaxio/jpa-query-by-example/blob/master/src/main/java/com/jaxio/jpa/querybyexample/SearchParameters.java) class
which can be passed as a parameter to the accountRepository's methods.

Let's make the first query above `case insensitive` and let's add an `ORDER BY`.

```java
Account example = new Account();
example.setLastName("Jagger");
SearchParameters sp = new SearchParameters().caseSensitive().orderBy(OrderByDirection.ASC, Account_.lastName);
List result = accountRepository.find(example, sp);
```

Note the usage of the
[Account_](https://github.com/jaxio/jpa-query-by-example/blob/master/src/test/java/demo/Account_.java)*
static metamodel, which helps you to keep your query related Java code strongly typed.

At the SQL level, the resulting FROM clause now looks like this:

```sql
from
ACCOUNT account0_
where
lower(account0_.LAST_NAME)=?
order by
account0_.LAST_NAME asc
```

#### Pagination

In most web application we need to paginate the query results in order to save resources. In the query below, we retrieve only
the 3rd page (we assume a page lists 25 rows). The first result is the 50th element and we retrieve at most 25 elements.

```java
Account example = new Account();
example.setLastName("Jagger");
SearchParameters sp = new SearchParameters().orderBy(OrderByDirection.ASC, Account_.lastName) //
.first(50).maxResults(25);
List result = accountRepository.find(example, sp);
```

At the SQL level, the resulting FROM clause now looks like this (we use H2 database):

```sql
from
ACCOUNT account0_
where
account0_.LAST_NAME=?
order by
account0_.LAST_NAME asc limit ? offset ?
```

#### LIKE and String

For strings, you can globally control whether a `LIKE` should be used and where the `%` wildcard should be placed. For example, adding :

```java
example.setLastName("Jag");
SearchParameters sp = new SearchParameters().startingLike();
```

to our example above would result in

```sql
account0_.LAST_NAME LIKE 'Jag%'
```

#### Multiple criteria

Until now, we have worked only with one property, lastName, but we can set other properties, for example:

```java
Account example = new Account();
example.setLastName("Jag");
example.setBirthDate(new Date());
SearchParameters sp = new SearchParameters().orderBy(OrderByDirection.ASC, Account_.lastName).startingLike();
List result = accountRepository.find(example, sp);
```

By default, the FROM clause uses a `AND` predicate.

```sql
from
ACCOUNT account0_
where
account0_.BIRTH_DATE=?
and (
account0_.LAST_NAME like ?
)
order by
account0_.LAST_NAME asc
```

To use instead `OR`, use the `.orMode()`, as follow:

```java
SearchParameters sp = new SearchParameters().orMode().orderBy(OrderByDirection.ASC, Account_.lastName).startingLike();
```

And this time we get:

```sql
where
account0_.LAST_NAME like ?
or account0_.BIRTH_DATE=?
order by
account0_.LAST_NAME asc
```

#### Is that all ?

Not really, we have just scratched the surface. For the moment, we have covered only rather simple queries.
While simplicity is key, it is often not sufficient. What about date or number range queries ? What about associated entities ? etc.

### Beyond Query By Example

#### Mixing Query by Example and Range Query.

Now, let's imagine that you also want to restrict the query above to all accounts having their date of birth between 1940 and 1945 included.
Of course, the entity does not have the appropriate property (from & to).
For this reason, we introduce an additional
[Range](https://github.com/jaxio/jpa-query-by-example/blob/master/src/main/java/com/jaxio/jpa/querybyexample/Range.java)
parameter.

Here is an example:

```java
Account example = new Account();
example.setLastName("Jagger");

Calendar from = Calendar.getInstance();
from.set(1940, 0, 1);

Calendar to = Calendar.getInstance();
to.set(1945, 11, 31);

Range birthDateRange = Range.newRange(Account_.birthDate);
birthDateRange.from(from.getTime()).to(to.getTime());

SearchParameters sp = new SearchParameters().range(birthDateRange);
List result = accountRepository.find(example, sp);
```

Note that you can add ranges of any type: Integer, Long, LocalDate (joda time), BigDecimal, etc...

This codes leads in fine to following `FROM` clause:

```sql
from
ACCOUNT account0_
where
(
account0_.BIRTH_DATE between ? and ?
)
and account0_.LAST_NAME=?
```

Here is a variation of the same example (depends on need, taste and color :-):

```java
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date from = dateFormat.parse("1920-12-01");
Date to = dateFormat.parse("1974-12-01");

SearchParameters sp = new SearchParameters().range(from, to, Account_.birthDate);
List accountList = accountRepository.find(sp);
```

#### Query all string properties in a OR clause

To find all entities having at least one of their String property matching a given value, use the `searchPattern` method.

Here is an example:

```java
SearchParameters sp = new SearchParameters().searchMode(SearchMode.STARTING_LIKE).searchPattern("Jag");
List result = accountRepository.find(sp);
```

The FROM clause now includes all string columns:

```sql
from
ACCOUNT account0_
where
or account0_.LAST_NAME like ?
or account0_.USERNAME like ?
```

#### Property Selector

In order to construct a `OR` clause for a given property we use the `PropertySelector` class.

Here is an example:

```java
PropertySelector lastNameSelector = PropertySelector.newPropertySelector(Account_.lastName);
lastNameSelector.setSelected(Arrays.asList("Jagger", "Richards", "Jones", "Watts", "taylor", "Wyman", "Wood"));

SearchParameters sp = new SearchParameters().property(lastNameSelector);

List result = accountRepository.find(sp);
```

Here is the corresponding FROM clause:

```sql
from
ACCOUNT account0_
where
account0_.LAST_NAME='Jagger'
or account0_.LAST_NAME='Richards'
or account0_.LAST_NAME='Jones'
or account0_.LAST_NAME='Watts'
or account0_.LAST_NAME='Taylor'
or account0_.LAST_NAME='Wyman'
or account0_.LAST_NAME='Wood'
```

Note that if you use JSF2 with PrimeFaces, you can directly pass a `PropertySelector` to a multiple autoComplete component's value property.
This way, the autoComplete component fills the PropertySelector. Here is how:

```xml

```

Here is a snapshot:

![property selector](https://github.com/jaxio/jpa-query-by-example/blob/master/src/img/property-selector.png)

PrimeFaces uses the `setSelected(List selection)` method to fill the lastNameSelector.

#### Mix it all

Remember, you can mix all the example we have seen so far.
You can have in a single query having multiple ranges, multiple property selector, multiple properties set on the example entity, etc.

This gives you great power ;-)

#### Query By Example on association

The `Account` entity has a `@ManyToOne` association with the `Address` entity.

Here is how we can retrieve all accounts pointing to an Address having its `city` property set to "Paris":

```java
Account example = new Account();
example.setHomeAddress(new Address());
example.getHomeAddress().setCity("Paris");
List result = accountRepository.find(example);
Assert.assertThat(result.size(), is(2));
```

The FROM clause uses a JOIN:

```sql
from
ACCOUNT account0_ cross
join
ADDRESS address1_
where
account0_.ADDRESS_ID=address1_.ID
and address1_.CITY='Paris'
```

Enjoy!

## License

The JPA Query By Example Framework is released under version 2.0 of the [Apache License][].

[Apache License]: http://www.apache.org/licenses/LICENSE-2.0