QueryDSL – A JPA Specification Alternatives
この記事の目次
Overview
QueryDSL is a powerful and flexible Java library designed to facilitate the creation of type-safe queries for various data sources, including JPA, SQL, MongoDB, and more. By leveraging Java’s type system, QueryDSL enables developers to build queries in a way that minimizes syntax errors and enhances code readability.
Furthermore, QueryDSL can integrate with Spring boot via this extension.
Why should we use it?
1. Type Safety
QueryDSL provides a type-safe querying mechanism, which means that the queries you create are checked at compile-time rather than at runtime. The compiler can give you feedback immediately when you make a mistake in query construction. This also reduces the risk of runtime errors.
Without QueryDSL, you might write a query string like this:
String query = "SELECT * FROM User WHERE age > 30";
With QueryDSL, you can create a query using a type-safe API:
QUser user = QUser.user;
JPAQuery<?> query = new JPAQuery<Void>(entityManager);
List<User> users = query.select(user) .from(user) .where(user.age.gt(30)) .fetch();
As you can see the QUser class is automatically generated by the QueryDSL library to support a lot of functionalities, which are pre-built and mapped to functions in SQL one by one when we build a certain query.
2. Readability and maintainability
With QueryDSL, you write queries in a fluent Java-based API which often reads like natural language. This improves the readability of your code and makes it easier to maintain. The expressive query language allows you to create complex queries more intuitively which are compared to traditional string-based query approaches. This makes it easier to implement than JPA Specifications. In JPA Specifications, you will need 3 mandatory arguments: Root, CriteriaQuery and CriteriaBuilder to pass into toPredicate to build the query. Therefore, your code is more complex, hard to maintain and reduces a lot of readability.
3. Reduced risk of SQL injection
Since queries are built using Java code rather than raw SQL strings, it’s more difficult for malicious input to be injected into the databases. Therefore, they will be secured safely.
4. Support complex queries
The author designed QueryDSL to be able to handle complex querying scenarios. It supports a lot of operations like join, subquery, and aggregation.
5. Extensible and Customizable
QueryDSL itself is highly extensible. You can create custom query predicates, functions and types to fit your specific needs. This flexibility allows you to adapt the library with various requirements and scenarios. For example, although the library does not have the MATCH function, which is used for full-text search, we can extend it from Query<Q extends Query<Q>> ourselves and pass into the necessary arguments.
6. Supportive documentation and community
The documentation is written in detail so it’s really helpful for various scenarios. In addition, there are a lot of tutorial blogs written in its community.
You can check them here
7. Projection
This is absolutely a pain for JPA Specification because after doing much research, I have come to the conclusion that JPA Specification does not support projection to select the data we want from databases. We will need to convert the queried data into streams and map them to a DTO. It causes bad performance if the data from databases is too huge. Therefore, QueryDSL has come to provide projection with type-safety.
Sample code:
import com.querydsl.jpa.impl.JPAQuery;
import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.List; public class UserRepository { @PersistenceContext private EntityManager entityManager;
public List<UserProjection> getUserProjections(int minAge) { QUser user = QUser.user; JPAQuery<UserProjection> query = new JPAQuery<>(entityManager); List<UserProjection> projections = query.select(new QUserProjection(user.firstName, user.age)) .from(user).where(user.age.gt(minAge)).fetch(); return projections; } } |
Note: This code is only for demonstration, to show the visualization. The sample code will be updated in the next blog.
8. Integration with multiple data sources.
QueryDSL supports a wide range of backends including JPA (Java Persistence API), SQL (via JDBC), MongoDB, and more. This makes it versatile and adaptable to various types of data sources. Whether you’re using a relational database or a NoSQL database, QueryDSL can handle it via an abstract class called AbstractSQLQuery.
In the other hand
In spite of giving a lot of pros in development, it may not be updated quickly. The latest version is 5.1.0 and it has just been updated in 2024. The previous version was 2021. There are also a lot of vulnerabilities because the core inside still uses old dependencies.
Conclusion
Lastly, based on scenarios, we will choose the most suitable option.
For complex and flexible queries, we can go with QueryDSL. It provides advanced features, readability and maintainability. It’s also especially useful if we work with multiple types of data sources.
For standard JPA integration, we can stick with JPA Specifications. It is useful for applications where we want to avoid using external libraries and leverage the built-in JPA functionalities.
Thank you for your reading :).
カテゴリー: