If you develop systems using Java with JPA, you have probably faced the need to execute queries that return only a subset of attributes from a given entity.
At first glance, this may seem like a simple problem. However, when not handled properly, systems tend to accumulate unnecessary queries or queries overloaded with attributes that will never be used.
In many real-world scenarios, a developer needs to retrieve only the “id” and “name” of an entity. Due to the size and complexity of the system, it often becomes difficult to identify whether a query already exists that returns exactly this data. In other cases, developers end up reusing methods that load the entire entity, only to later extract the few attributes that are actually required.
It is within this context that ProjectionQuery comes into play. Its goal is to simplify and organize projection-based queries, providing a clearer and more expressive way to select only the data that the application truly needs.
How ProjectionQuery Works in Practice
After adding the dependency to your project, you only need to create a class (or a record) and define which fields should be projected.
@Projection(of = Customer.class)
public record CustomerBasicData(
@ProjectionField Long id,
@ProjectionField String name,
@ProjectionField("address.city.name") String city,
@ProjectionField("address.city.state.name") String state
) { }
This class acts as the final representation of the query. Regardless of how many attributes exist in the Customer entity, only the id, name, city, and state fields will be selected from the database.
Note that city and state are nested attributes, retrieved through relationships defined in the Customer entity.
The final SQL (simplified) generated for this query will look something like the following:
select
id,
name,
city.name,
state.name,
from customer
inner join address on customer.address = address.id
inner join city on address.city = city.id
inner join state on city.state = state.id
Now that we have our projection class defined, we can use the ProjectionProcessor, which acts as the execution engine for the queries.
There are two main ways to execute this query:
In the following examples, we assume a JPA context in which the
ProjectionProcessorreceives anEntityManagerinstance, responsible for executing the queries.
Simplified execution
ProjectionProcessor processor = new ProjectionProcessor(entityManager);
List<CustomerBasicData> customers = processor.execute(CustomerBasicData.class);
Using the ProjectionQuery class
The ProjectionQuery class helps build more advanced queries, allowing the addition of filters, sorting, pagination, and other configurations.
ProjectionProcessor processor = new ProjectionProcessor(entityManager);
ProjectionQuery<Customer, CustomerBasicData> query = ProjectionQuery
.fromTo(Customer.class, CustomerBasicData.class)
.filter("address.city.name", ProjectionFilterOperator.EQUAL, "São Paulo")
.order("name", OrderDirection.ASC)
.paging(0, 20)
.distinct();
List<CustomerBasicData> customers = processor.execute(query);
ProjectionQuery can be used both independently and integrated into Spring Boot applications.
For more details, additional examples, and full documentation, please refer to the project page on GitHub.
Top comments (4)
This is a really thoughtful approach to simplifying JPA queries! I like how ProjectionQuery makes it easy to select only the fields you need without overloading the system. I can see this being especially useful in larger applications where efficiency and clarity are important. I’m definitely interested in trying this out in a project and seeing how it improves query management.
I’m really glad you’re interested in how ProjectionQuery works. This was a pain point I personally experienced for a long time across several projects, and today I’ve already been able to adopt it in a few of them.
There’s definitely still a lot of room for improvement, but real usage and feedback are what help shape the project the most. Once you get a chance to try it out, feel free to share your thoughts - any suggestion or criticism is more than welcome and truly valuable.
Your material is very good
Great content.