You will learn how to use Jakarta Data to store data as well as the simple, yet powerful options it provides for querying data. This will include writing queries that are created from the method name, as well as queries using annotations. Finally, you will learn about the Jakarta Data Query Language (JDQL).
The application demonstrates a number of different queries that are possible with Jakarta Data. Once you have completed the guide you can try writing your own queries with provided sample data.
Jakarta Data is a new data access specification being released with Jakarta EE 11. It provides a new API for simplified data access. Jakarta Data follows the Repository design pattern for data access, and allows for implementations which may support both SQL and NoSQL data stores. It also provides enhanced type safety with a Static Metamodel.
Go to the http://localhost:9080 URL to see all the application data. You will see a column of queries, some of which will accept input. To start find the findAll
query with no inputs. Selecting this will return all of the packages similar to the following:
id = 1 length = 10 width = 20 height = 10 destination = Rochester
id = 2 length = 30 width = 10 height = 10 destination = Austin
id = 3 length = 5 width = 10 height = 5 destination = RTP
id = 4 length = 24 width = 15 height = 6 destination = Rochester
id = 5 length = 15 width = 7 height = 2 destination = Austin
id = 6 length = 8 width = 5 height = 3 destination = Rochester
id = 7 length = 16 width = 3 height = 15 destination = RTP
id = 8 length = 2 width = 15 height = 18 destination = Rochester
You can then try some of the queries which use input to see how it changes the returned packages.
In Jakarta Data an entity defines the structure for persisting a piece of data. This structure is represented by a Java object and its associated fields. Start by creating a simple record class.
Create thePackage
class.src/main/java/io/openliberty/guides/data/Package.java
Package.java
link:finish/src/main/java/io/openliberty/guides/data/Package.java[role=include]
This is a simple entity, with a few fields.
Create thePackages.java
class.src/main/java/io/openliberty/guides/data/Packages.java
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]
Repositories in Jakarta Data provide a simplified means for interacting with persistent data. Jakarta Data provides several built in Repositories with common methods which can be extended. The CrudRepository
interface provides methods for Create, Read, Update, and Delete (CRUD) operations.
CrudRepository
provides two options for persisting entities, insert
and save
. The first option insert
will persist an entity only if the entity does not already exist. The save
option will persist and entity even if it already exists, allowing it to be used for updating entities as well. CrudRepository
provides a delete
method for removing entities. All three of these methods can be applied to a list of entites using insertAll
, saveAll
, and deleteAll
.
Package p1 = new Package(1, 10f, 20f, 10f, "Rochester");
packages.insert(p1);
packages.delete(p1);
#Update the `Packages.java` class.# `src/main/java/io/openliberty/guides/data/Packages.java`
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]
Query by Method Name allows you to write queries using a descriptive method name following a few intuitive rules on how the method name is structured. This allows methods like findByLengthGreaterThan(float length)
or findByHeightAndWidth(float height, float width))
to automatically be turned into queries. Method names must start with an action like find
, delete
, or count
and can contain include operators (such as And
, Or
, and Not
) and conditions (such as GreaterThan
, LessThan
, and Between
).
For more information on all of the options available with Query by Method name, see the Query by Method Name Extension of the Jakarta Data specification.
#Update the `Packages.java` class.# `src/main/java/io/openliberty/guides/data/Packages.java`
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]
The @Find
annotation indicates that a method is a query which may return entities. Used in isolation it will
return all entities, but by using the @By
annotation, it can be limited to entities which match the value provided at runtime.
@Find
List<Package> getPackagesArrivingIn(@By("destination") String destination);
If a method name provided by a built in Jakarta Data repository class doesn’t align with your repository you can use annotations to provide your own method. For example, using the @Insert
annotation you can use add
instead of the insert
method inherited from CrudRepository
like this.
@Insert
void add(Package p);
This method is functionally equivalent to the insert
method provided by CrudRepository
.
To provide ordering annotatively, you can use the @OrderBy
annotation. The default ordering direction is ascending. To obtain all of the entities in a repository sorted by the height parameter, you could use the following.
@Find
@OrderBy("height")
List<Package> sortedByHeightAscending();
@OrderBy
can also return in descending order by specifying descending = true
in the annotation.
@OrderBy(value = "height", descending = true)
Jakarta Data provides the Sort
class and the @Limit
annotation to create queries which can be modified at runtime. It also includes a mechanism for pagination to better handle large sets of results.
#Update the `Packages.java` class.# `src/main/java/io/openliberty/guides/data/Packages.java`
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]
In addition to the @OrderBy
annotation it is possible to provide sorting at runtime. This is accomplished by adding a Sort
as a parameter to your query method.
@Find
List<Package> sorted(Sort<?> sortBy);
A Sort
can be created and provided at runtime using the Sort class’s static methods:
Sort sort = Sort.desc("height");
Jakarta Data queries can restrict the number of results returned at runtime by providing a Limit
object to the query.
List<Package> longestWithLimit(Limit limit);
Limits can be created and supplied using the Limit class’s static methods:
Limit limitTen = Limit.of(10);
Limits can also be used to access a range of results, such as starting at result 10 and ending at result 20:
Limit range = Limit.range(10,20);
When querying large amounts of data, paging is possible by providing a PageRequest
in a query.
@Find
Page<Package> allWithPaging(PageRequest pageRequest); // TODO: Provide more realistic examples(?) with limits?
A PageRequest
can be constructed with the PageRequest
class’s static methods. To request the first page with a page size of 20 results:
Page<Package> page = packages.allWithPaging(PageRequest.ofSize(20));
List<Package> results = page.content();
Navigating pages can be done from the Page
object:
Page<Package> anotherPage = packages.allWithPaging(page.nextPageRequest());
The @Query
annotation allows users to write queries using the Jakarta Data Query Language (JDQL). Complex queries can be written concisely using JDQL.
#Update the `Packages.java` class.# `src/main/java/io/openliberty/guides/data/Packages.java`
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]
@Query("WHERE length > :threshold OR height > :threshold OR width > :threshold")
List<Package> withDimensionLargerThan(float threshold);
Achieving the same query with Query by Method Name would result in a very long method name and additional parameters. JDQL is a strict subset of the Jakarta Persistence Query Language (JPQL), supporting named and ordinal parameters, as well as a number of operators.
@Query("WHERE length + width + height > ?1")
List<Package> withTotalDimensionOver(float threshold);
See the Jakarta Data specification for a more comprehensive overview of the Jakarta Data Query Language.