A Quick introduction to Querydsl compared to JPA 2 Criteria Api
Querydsl is a Java open-source project that supports statically typed queries. It provides a typesafe querying layer on top of JPA, JDO, JDBC and other backends through the use of various modules. The framework uses an annotation processor to generate Java types based on JPA entities. In this short article I’ll be focusing on Querydsl’s queries support for JPA and how it can be used as an alternative to Criteria Api. Of course the framework also allows to modify and delete records by JPAQueryFactory methods.
What is Querydsl?
Querydsl is a Java open-source project that supports statically typed queries. It provides a typesafe querying layer on top of JPA, JDO, JDBC and other backends through the use of various modules. The framework uses an annotation processor to generate Java types based on JPA entities. In this short article I’ll be focusing on Querydsl’s queries support for JPA and how it can be used as an alternative to Criteria Api. Of course the framework also allows to modify and delete records by JPAQueryFactory methods.
Project configuration(Maven option)
Maven dependencies:
<dependency>
<groupid>com.querydsl</groupid>
<artifactid>querydsl-apt</artifactid>
<version>4.2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupid>com.querydsl</groupid>
<artifactid>querydsl-jpa</artifactid>
<version>4.2.2</version>
</dependency>
Maven APT plugin configuration. The JPAAnnotationProcessor finds domain types annotated with the javax.persistence.Entity annotation and generates query types for them:
<plugin>
<groupid>com.mysema.maven</groupid>
<artifactid>apt-maven-plugin</artifactid>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputdirectory>target/generated-sources/java</outputdirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
Simple queries examples
In examples I’ll use entity:
@Entity
@Table(name = "Employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String surname;
private Long salary;
//getters and setters
}
Under the hood querydsl-apt will generate for us QEmployee.class that can be found in target/generated-sources/java in the same package as Employee. This class will be used to create queries.
Example query method for retrieving employees by name in Querydsl:
public List<employee> findEmployeesByName(String name) {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QEmployee qEmployee = QEmployee.employee;
return queryFactory.selectFrom(qEmployee)
.where(qEmployee.name.eq(name))
.fetch();
}
Same method in Criteria Api:
public List<employee> findEmployeesByName(String name) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<employee> criteriaBuilderQuery =
criteriaBuilder.createQuery(Employee.class);
Root<employee> employee = criteriaBuilderQuery.from(Employee.class);
Predicate predicate = criteriaBuilder.equal(employee.get("name"), name);
criteriaBuilderQuery.select(employee)
.where(predicate);
return entityManager.createQuery(criteriaBuilderQuery)
.getResultList();
}
We could also add many conditions(„and” case, our method will now have an extra parameter surname):
queryFactory.selectFrom(qEmployee)
.where(qEmployee.name.eq(name).and(qEmployee.surname.eq(surname)))
.fetch();
//alternative notation .where(qEmployee.name.eq(name),qEmployee.surname.eq(surname))
The same case with Criteria Api:
Predicate predicateName = criteriaBuilder.equal(employee.get("name"), name);
Predicate predicateSurname = criteriaBuilder.equal(employee.get("surname"),surname);
criteriaBuilderQuery.select(employee)
.where(criteriaBuilder.and(predicateName, predicateSurname));
//alternative notation criteriaBuilderQuery.where(predicateName, predicateSurname);
entityManager.createQuery(criteriaBuilderQuery)
.getResultList();
Ordering
In the following example we will receive the list in a descending order of the surname: First in Querydsl:
return queryFactory.selectFrom(qEmployee)
.where(qEmployee.name.eq(name), qEmployee.surname.eq(surname))
.orderBy(qEmployee.surname.desc())
.fetch()
Criteria Api:
criteriaBuilderQuery.select(employee)
.where(predicateName, predicateSurname)
.orderBy(criteriaBuilder.desc(employee.get("surname")));
return entityManager.createQuery(criteriaBuilderQuery)
.getResultList();
Aggregation
As an aggregation function example I have chosen sum(), we will sum all salaries from the Employee table:
Querydsl implementation:
public long findEmployeesSalarySum() {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QEmployee qEmployee = QEmployee.employee;
return queryFactory.select(qEmployee.salary.sum())
.from(qEmployee)
.fetchOne();
}
Criteria Api implementation:
public long findEmployeesSalarySum() {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<long> criteriaBuilderQuery = criteriaBuilder.createQuery(Long.class);
Root<employee> employee = criteriaBuilderQuery.from(Employee.class);
criteriaBuilderQuery.select(criteriaBuilder.sum(employee.get("salary")));
return entityManager.createQuery(criteriaBuilderQuery)
.getSingleResult();
}
Spring Data integration
An additional advantage in favor of Querydsl is that Spring provides integration with this framework through the QueryDslPredicateExecutor interface. It allows us to create simple queries using the spring repository and Predicates. The only thing we should do is to extend this interface in our spring data repository and we will receive additional methods where we can use Predicates.
The Spring Data repository that we will use:
@Repository
interface EmployeesRepository extends CrudRepository<employee, long=""> {
}
To enable Querydsl in our repository, we just need to extend from QuerydslPredicateExecutor.
@Repository
interface EmployeesRepository extends CrudRepository<employee, long="">, QuerydslPredicateExecutor<employee> {
}
Now we have access to a whole new set of methods:
Optional<t> findOne(Predicate predicate);
Iterable<t> findAll(Predicate predicate);
Iterable<t> findAll(Predicate predicate, Sort sort);
Iterable<t> findAll(Predicate predicate, OrderSpecifier<!--?-->... orders);
Iterable<t> findAll(OrderSpecifier<!--?-->... orders);
Page<t> findAll(Predicate predicate, Pageable pageable);
long count(Predicate predicate);
boolean exists(Predicate predicate);
Let’s adjust some of our previous examples to use the Spring Data repository with Querydsl. We will start with the first one – query all employees by name. The result of course will be the same as the previous one, but now we do not need to do much, we just provide Predicate to repository and that’s all.
public List<employee> findEmployeesByName(String name) {
QEmployee qEmployee = QEmployee.employee;
BooleanExpression predicate = qEmployee.name.eq(name);
return Lists.newArrayList(employeesRepository.findAll(predicate));
}
To add some ordering we need to use overloaded method and provide OrderSpecifier as a second parameter:
QEmployee qEmployee = QEmployee.employee;
BooleanExpression predicate = qEmployee.name.eq(name);
return Lists.newArrayList(employeesRepository.findAll(predicate, qEmployee.name.desc()));
Summary
At the beginning Hibernate was the first target language for Querydsl, but nowadays it supports JPA, JDO, JDBC, Lucene, Hibernate Search, MongoDB, Collections and RDFBean as backends. In this article we just cover a small part of the possibilities of using JPA. Those simple examples clearly show that the use of Querydsl is simpler, clearer than Criteria Api and very similar to sql queries. This is why we should:
- Easy to use
- Securing query syntax at all levels
- No Strings involved!
- Integrated with IDE—all properties, methods and operations can be expanded in the editor
- Provides an easy integration mechanism with Spring Data
Sources
- Querydsl website: http://www.querydsl.com/