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 the 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.
This application allows you to run queries against a set of shipping packages which have a variety of dimensions and destinations. 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 query data similar to the following:
ID Length Width Height Destination 1 10 20 10 Rochester 2 30 10 10 Austin 3 5 10 5 RTP 4 24 15 6 Rochester 5 15 7 2 Austin 6 8 5 3 Rochester 7 16 3 15 RTP 8 2 15 18 Rochester
You can then try some of the queries which use input to see how it changes which packages are returned. If you find the query named findByLengthGreaterThan and enter 25 in the box labeled length, you can then run the query by clicking the ➜ button. The result should look like the following, with only lengths greater than 25:
ID Length Width Height Destination 2 30 10 10 Austin 16 38 16 25 Markham
Navigate to the start directory to begin.
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. The entity you will work with in this guide is a record class called Package.java, which contains a few fields.
Package.java
link:finish/src/main/java/io/openliberty/guides/data/Package.java[role=include]Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]A common pattern with repositories is to use the plural of the entity, so the repository you will be working with in this guide is called Packages.java. Repositories in Jakarta Data provide a simplified means for interacting with persistent data. They are annotated with the @Repository annotation. Jakarta Data provides several built-in repositories with common methods which can be extended with application-specific queries. 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 an entity even if it already exists, allowing it to be used for updating entities as well. CrudRepository provides a delete method for removing entities.
Package p1 = new Package(1, 10f, 20f, 10f, "Rochester");
packages.insert(p1);
packages.save(p1);
packages.delete(p1);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.
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]Update thePackages.javaclass.src/main/java/io/openliberty/guides/data/Packages.java
This allows methods like findByLengthGreaterThan(float length) or findByHeightBetween(float minHeight, float maxHeight) to automatically be turned into queries. Method names must start with an action like find, delete, or count and can 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.
Queries can also be written using method annotations. This allows for additional flexibility in the queries as well as the method names.
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]Update thePackages.javaclass.src/main/java/io/openliberty/guides/data/Packages.java
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. The getPackagesArrivingIn method demonstrates using the @Find and @By
annotations to return all packages that are destined for a specific location.
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 create a method named add instead of the insert method inherited from CrudRepository.
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. The sortedByHeightAscending method demonstrates how to obtain all of the packages in the repository sorted by the height parameter.
@OrderBy can also return in descending order by specifying descending = true in the annotation:
@OrderBy(value = "height", descending = true)Jakarta Data provides the Sort and Limit classes to create queries which can be modified at runtime. It also includes a mechanism for pagination to better handle large sets of results.
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]Update thePackages.javaclass.src/main/java/io/openliberty/guides/data/Packages.java
In addition to the @OrderBy annotation it is possible to provide sorting at runtime. This is accomplished by adding a Sort parameter to your query method like in the sorted method.
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 entities returned at runtime by providing a Limit object to the query as shown in the shortestWithLimit method.
This method will return the shortest packages in order from shortest to longest, but limited to only the specified number of results.
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 entities, such as starting at the 10th entity and ending at the 20th:
Limit range = Limit.range(10,20);When querying large amounts of data, paging is possible by adding a PageRequest parameter to your query method like in the all method. This method will return all of the packages, but with the results paginated based on the provided PageRequest.
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.all(PageRequest.ofSize(20));
List<Package> results = page.content();Navigating pages can be done from the Page object:
Page<Package> anotherPage = packages.all(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. JDQL is a strict subset of the Jakarta Persistence Query Language (JPQL).
Packages.java
link:finish/src/main/java/io/openliberty/guides/data/Packages.java[role=include]Update thePackages.javaclass.src/main/java/io/openliberty/guides/data/Packages.java
For an example of a complex query simplified using JDQL, take a look at withAnyDimensionLargerThan.
This query checks for packages where the length, height, or width are larger than a threshold, which is provided as a parameter in the function. The threshold parameter is referenced in the query using :threshold. Achieving the same query with Query by Method Name would result in a very long method name and additional parameters.
JDQL supports both named and positional parameters, as well as a number of operators. This is shown in the withTotalDimensionOver query, which sums the length, width, and height of each package to check if the total exceeds a provided threshold. Unlike the named parameter syntax (:threshold) used in the previous example, this query uses positional parameter syntax with ?1 to reference the first method parameter.
See the Jakarta Data specification for a more comprehensive overview of the Jakarta Data Query Language.
Because you are running the application in dev mode, any changes you make to the code are automatically picked up. You’re now ready to test out your queries in the browser.
Point your browser to the http://localhost:9080 URL to access the application. You can select different queries from the list and provide input parameters to see how Jakarta Data retrieves the package data.
Try experimenting with different query parameters to see how the results change. For example, use the findByLengthGreaterThan query with different length values to filter packages by length.
Experiment by creating your own queries. Use the existing ones in the Packages class as a starting point and then test them out in the sample application. For example, you could add a query to find packages arriving at a specific destination, which is ordered by width:
@Find
@OrderBy("width")
List<Package> byWidthArrivingIn(@By("destination") String destination);