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. As you proceed through the guide, you will be able to write 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.
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 can see a column of queries, some of which accept input. To start, find the findAll query with no inputs. Selecting this returns 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 looks 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 used 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]The repository used in this guide is called Packages.java. Repositories in Jakarta Data provide a simplified means for interacting with persistent data. Jakarta Data’s CrudRepository interface provides built-in methods for Create, Read, Update, and Delete (CRUD) operations, so all you need to add are queries specific to your application’s needs.
For more information on Jakarta Data repositories, see the documentation here: TODO link
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.
The findByLengthGreaterThan method finds packages where the length exceeds the value you provide. If you pass 15, it returns packages with lengths greater than 15 from the sample data. The findByHeightBetween method finds packages within a specific height range. If you pass 5 and 15, it returns packages with heights between 5 and 15, inclusive.
For more information see the JavaDoc section on Query by Method Name
Because you are running the application in dev mode, your changes are automatically picked up. Refresh the application in your browser at http://localhost:9080 to see the new queries you just added. Try out the findByLengthGreaterThan query by entering different length values, or experiment with the findByHeightBetween query by providing minimum and maximum height values.
Now that you have seen how Query by Method Name works, try creating your own queries following the same pattern. Here are some ideas to get you started:
-
Find all packages going to a specific destination
-
Find packages narrower than a specified width
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
returns 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.
For example, when you call this method with Rochester, it returns the 6 packages from the sample data that are going to Rochester.
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.
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.
This returns all packages ordered from shortest to tallest, starting with the package with height 2, then 3, and so on up to the tallest package with height 25.
Refresh the application in your browser to see the new queries you just added. Try out the getPackagesArrivingIn query by entering different destination names, or experiment with the sortedByHeightAscending query to see the packages ordered by height.
Now that you have seen how to use annotations for queries, try creating your own annotated methods. Here are some ideas to get you started:
-
Find packages with a specific width using
@Findand@By -
Create a method that returns packages ordered by length
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.
For example, you can sort by length to see packages from shortest (#2) to longest (#38), or sort by destination to see them alphabetically (Austin, Markham, RTP, Rochester).
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 returns the shortest packages in order from shortest to longest, but limited to only the specified number of results. For example, if you pass Limit.of(3), it returns only the 3 shortest packages (with heights of 2, 3, and 5).
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 returns all of the packages, but with the results paginated based on the provided PageRequest.
For example, PageRequest.ofSize(5) returns the first 5 packages, and you can request subsequent pages to see the remaining packages from the sample data.
Refresh the application in your browser to see the new queries you just added. Try out the sorted query by providing different sort parameters, or experiment with the shortestWithLimit query to see how limiting affects the results.
Now that you have seen how sorting, limiting, and paginating work, try creating your own queries using these features. Here are some ideas to get you started:
-
Find packages going to a specific destination with sorting
-
Get a limited number of the widest packages
-
Create a paginated query for packages by destination
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.
For example, if you pass 20, it returns packages where at least one dimension exceeds 20, such as package #2 (length 30) or package #16 (length 38, height 25).
The withTotalDimensionOver query sums the length, width, and height of each package to check if the total exceeds a provided threshold. This query uses positional parameter syntax with ?1 to reference the first method parameter.
For example, passing 50 returns packages where length + width + height > 50. Package #16 (38 + 16 + 25 = 79) would be included in the results.
See the Javadoc for the Query Annotation for more information on JDQL.
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);