How JPAstreamer Can Help You Write Type-Safe Hibernate Code Without Unnecessary Complexity
Learn how to use Hibernate in a type-safe manner without unnecessary complexity using standard Java Streams.
Join the DZone community and get the full member experience.Join For Free
For the past 10 or so years, JPA has been one of the most popular ways of accessing a database within the Java ecosystem. Even if you haven't heard of JPA directly, there is a high likelihood that you've heard of or even used one of the most popular implementations of the JPA API - Hibernate.
The reason why JPA is so popular is no secret - most of the inconveniences are abstracted from the user, making the API very intuitive to use. Let's say that in some random database exists a table called person with the following structure:
To represent said table via JPA, users would typically create a class called Person which looks like this:
Once this initial setup is finished, users can use the EntityManager to create and execute queries. Let's say we wanted to retrieve information about a person with the id of 10. With JPA we could do something like this:
I consider this to be a very efficient approach considering most of the heavy lifting is done for us. But not everything is sunshine and rainbows. Since we're creating queries using raw SQL, syntax errors are inevitable most of the time.
To combat this, a type-safe way of creating queries was introduced in JPA 2.0, named the Criteria API. Let's see how we can recreate the query above using the Criteria API:
The difference between the 2 approaches is quite evident. While we're able to create and execute queries in an almost type-safe way (e.g. root.get("personId") is not safe), our code has gotten substantially more complex. Ideally, we want to construct type-safe queries in a manner that doesn't introduce unnecessary complexity in our codebase.
In this article, I'll be showing you how to achieve this goal using JPAstreamer, a lightweight JPA extension that allows us to construct queries in a type-safe way using Java Streams.
Before we can start using JPAstreamer we must install it as a dependency in our project. If you're using Maven as your build tool, add the following dependency:
If you're using Gradle as your build tool, add the following dependency and annotation processor:
To get access to a JPAstreamer instance, use the following bit of code:
Make sure you're using the correct persistence unit name when creating a JPAStreamer instance. I've called mine my-persistence-unit in the template above, so that's the name I will be passing to JPAStreamer::of.
Creating a Stream
The JPAStreamer instance has a stream() method which you can use to create a Stream for your entities. As an example, I'll use the Person entity created above as a Stream source:
Once you've obtained a Stream for our Entity, you can start writing queries using the Java Stream API.
Executing a Query
As a start, let's try to recreate the JPA query we created at the beginning of the article, but this time we'll use the Stream API:
By terminating this Stream a series of optimizations and translations take place which in the end create and execute the following SQL Query:
A More Complex Example
The example that we've been working with so far is rather simple, so let's try creating a JPA query that is a bit more complex in nature:
A lot of things are happening here, so let's break them down. The query will look for Person entities whose first name starts with the letter C and the last name starts with the letter M. Alongside these conditions, the results will be sorted by the last name and first name. Additionally, an offset of 5 is applied to the query, meaning the first 5 elements of the result will be skipped. Also, the result is limited to 10 entities.
Now let's see how this can be replicated using the Stream API:
The 2 approaches produce identical results, but the declarative style of programming that Java Streams provide us with makes our code less complex, allowing us to develop software a lot more efficiently.
Optional: Setting up a Persistence Unit
While this is not a JPA tutorial, it is important to mention that you must have a persistence unit ready. If you're adding JPAstreamer to an existing JPA project, then you probably have a persistence unit already created.
For those of you who are starting from scratch or have never configured JPA before, you must create a file named persistence.xml in your resources/META-INF folder. I'll provide you with a template for the persistence.xml file which you can modify:
JPA is a great API when it comes to database access, but its usage can easily lead to unnecessary complexity. With JPAstreamer, you're able to utilize JPA while keeping your codebase clean and maintainable.
Published at DZone with permission of Mislav Milicevic. See the original article here.
Opinions expressed by DZone contributors are their own.