Querydsl is a Java library that allows for type-safe query building for Java persistence APIs like JPA and data sources like SQL databases. It provides a fluent, domain-specific query language that avoids issues with traditional string-based queries. Querydsl handles query validation at compile time, avoids syntax errors, and supports auto-complete in IDEs. It also works across different backends with a consistent API and allows custom extensions.
2. Before Querydsl
● Queries as strings within code
TypedQuery<Person> query = em.createQuery(
"select person from Person person " +
"where person.firstName = ?1", Person.class);
query.setParameter(1, "Max");
List<Person> persons = query.getResultList();
● Must remember query syntax, domain classes,
properties and relationships
● Syntax reference always at hand
● Domain model/schema reference at hand
● High cognitive overhead
● Error-prone
3. Before Querydsl
● Dynamic query building by string concatenation
● Very hard with multiple joins, ordering and complex
conditionals depending on actual parameters
StringBuilder where = new StringBuilder();
if (firstName != null)
where.append("person.firstName = :firstName");
...
TypedQuery<Person> query = entityManager.createQuery(
"select person from Person person where " + where,
Person.class);
if (firstName != null) query.setParameter("firstName", firstName);
...
List<Person> persons = query.getResultList();
4. Before Querydsl
● Query syntax validation by execution is slow and
breaks the flow
● Each back-end has its own query language and API
● SQL-like for JPA and JDO, but not for MongoDB
and Lucene
● Verbose parameter binding by name or position to
parameter placeholders of a prepared statement
● Or risk injection attack if parameters are directly
concatenated to query
5. Before Querydsl
● Hibernate Criteria API as an alternative?
● Better for dynamic queries and has easier
parameter binding, but...
● Lacking expressivity, unintuitive, verbose,
cognitive overhead for schema if not for syntax,
not type-safe, slow validation...
● Hibernate with three query languages to
master with different focuses and expressivity
6. Querydsl
● Domain model specific type-safe query language
● Compile time query validation
● Instant feedback on query errors
● Compact and intuitive fluent syntax
● Syntactically close to SQL
● Great for dynamic query building
● Supports multiple back-ends and query languages
with consistent query API
● JPA/Hibernate, JDO, SQL, Lucene, Mongodb...
● Once you know basics of one language, you know
basics of all SQL-like Querydsl languages
7. Querydsl
● Autocomplete with Java IDEs
● No need to remember exact syntax
● No need to remember property names
● Better support for domain model refactoring
● When domain changes, queries show compile
errors
● Autocomplete helps fixing those
● Developed for real-life projects, e.g. Balancion and
Cyclos
● Business friendly license (Apache 2.0)
8. Querydsl
● Development started in 2007 with public releases since
2008
● Querydsl statistics:
● Approximately 24 000 LOC
● Test Code coverage about 75% (target 80%)
● Sonar reports
● FindBugs with extra annotations (@Nullable)
● Discussions about standardisations
● JDO/DataNucleus started with Querydsl...
9. Querydsl usage
● Create your variables
QPerson.person // default variable
new QPerson("myPerson") // custom variable
● Create your query
JPAQuery, HibernateQuery, SQLQueryImpl etc
● Populate your query
from, where, groupBy, having, orderBy
● Get the results
count, iterate, list, uniqueResult
10. Querydsl usage
● All expressions can be reused, immutables with
caching – except BooleanBuilder and a few others
● Queries, sub queries and BooleanBuilder are stateful
builder with cascading methods
11. Overview of JPAQuery signature
from
Query sources
innerJoin, join, leftJoin, fullJoin, on
Join elements
join(source, alias) [.on(source.prop.eq(alias.prop))]
where
Query filters, varargs for intersection (and)
and(), or(), allOf(), anyOf()
12. Overview of JPAQuery signature
groupBy
Group by arguments in varargs form
having
Having filter of the "group by” as an varags array of Predicate
expressions.
orderBy
Ordering of the result as an varargs array of order expressions.
asc() and desc() on numeric, string and other comparable expression
limit, offset, restrict
Paging of the result
Limit for max results and Offset for skipping rows and
Restrict for defining both in one call
13. Overview of JPAQuery signature
list
Get the results as a typed List
listResults
Get the results as a typed List and total row count for paging
iterate
Get the results as a typed Iterator
count
Get the row count as a long
uniqueResult
Get a typed single row result
14. Simple example
QPerson person = QPerson.person;
JPAQuery query = new JPAQuery(entityManager);
List<Person> persons = query.from(person)
.where(
person.firstName.eq("John"),
person.lastName.eq("Doe"))
.list(person);
=>
select person from com.acme.Person person
where person.firstName eq = ?1 and person.lastName = ?2
15. Order
// Get persons ordered by last name and first name (desc)
query.from(person)
.orderBy(person.lastName.asc(), person.firstName.desc())
.list(person);
=>
select person from Person person
order by person.lastname asc, person.firstName desc
16. Order
// Get persons ordered by women first
query.from(person)
.orderBy(person.gender
.when(Gender.FEMALE).then(0)
.otherwise(1).asc())
.list(person);
=>
select person from Person person
order by case person.gender = Gender.FEMALE then 0 else 1 end asc
17. Grouping
// Get person counts grouped by last name
query.from(person)
.groupBy(person.lastName)
.list(person.lastName, person.count());
=>
select person.lastName, count(person) from Person person
group by person.lastName
18. Subqueries
//Get persons with max child count
QPerson parent = new QPerson("parent");
query.from(person)
.where(person.children.size().eq(
new JPASubQuery().from(parent)
.uniqueResult(parent.children.size().max())
)).list(person);
=>
select person from Person person
where person.children.size() = (
select max(parent.children.size()) from Person parent)
19. Constructor projection
// DTO class with @QueryProjection constructor annotation
public class PersonInfo {
long id;
String name;
@QueryProjection
public PersonInfo(long id, String name) {
this.id = id;
this.name = name;
}
}
// List PersonInfo DTOs
List<PersonInfo> infos = query.from(person)
.list(new QPersonInfo(person.id,
person.lastName.concat(", ”).concat(person.firstName)));
20. Tuple projection
// List ages of persons
List<Tuple> tuples = query.from(person)
.list(new QTuple(
person.lastName,
person.firstName,
person.yearOfBirth));
for (Tuple tuple : tuples){
// Typed access to mapped query results!
String name = tuple.get(person.firstName) +
" " + tuple.get(person.lastName);
int age = tuple.get(person.yearOfBirth)
- getCurrentYear();
System.out.println(name + " is " + age + " years");
}
21. BooleanBuilder
● Helper for building complex Boolean expressions
dynamically
BooleanBuilder nameDisjunction = new BooleanBuilder();
for (String name : names) {
nameDisjunction.or(person.firstName.like(name));
nameDisjunction.or(person.lastName.like(name));
}
query.where(nameDisjunction);
22. Update
// Set firstName of all Does to John
long updatedRowCount =
new JPAUpdateClause(getEntityManager(), person)
.set(person.firstName, "John")
.where(person.lastName.eq("Doe"))
.execute();
=>
update Person person
set person.firstName = ?1
where person.lastName = ?2
23. Delete
// Delete all John Does
long updatedRowCount =
new JPADeleteClause(getEntityManager(), person)
.where(person.lastName.eq("Doe"),
person.firstName.eq("John"))
.execute();
=>
delete Person person
where person.lastName = ?1 and person.firstName = ?2
24. Querydsl extensions
● Customize the code generation
● @QueryType(PropertyType.NONE)
● Non searchable
● @QueryType(PropertyType.SIMPLE)
● Equality comparisons only (eq, ne, in)
● Custom query classes
● Extend abstract super classes and preserve fluent
API
● Custom expressions
● Static delegate methods with @QueryDelegate
● Template expressions for e.g. custom SQL
functions
25. Querydsl extensions
● Query serialization can be customized
● Works for JPA, JDO and SQL
● SQL dialects
● Overriding default templates (e.g.
String#startsWith with like or regexp or...)
● Expression DSL can be replaced
● E.g. Querydsl for Scala
● Custom back-ends
● Lucene (10 classes) + Mongodb (6 classes)
26. Delegate methods
public class MyQueryExtensions {
@QueryDelegate(Date.class)
public static NumberExpression<Integer> yearAndMonth(DateTimePath<Date> date) {
return date.year().multiply(100).add(date.month());
}
}
=>
package ext.java.util;
...
public class QDate extends DateTimePath<java.util.Date> {
...
public NumberExpression<Integer> yearAndMonth() {
return MyQueryExtensions.yearAndMonth(this);
}
}
28. Custom query classes
public class PersonQuery extends AbstractJPAQuery<PersonQuery> {
final QPerson person = QPerson.person;
public PersonQuery(EntityManager em) {
super(em);
from(person);
}
public PersonQuery nameMatches(String name) {
return where(person.firstName.like(name)
.or(person.lastName.like(name)));
}
}
29. JPA 2.0 Criteria vs Querydsl
● JPA 2 Criteria is the standard for type-safe
queries in JPA, but Querydsl is in our opinion
superior in many ways
● Easier and less verbose syntax
● Customizable
● Supports multiple back-ends – not just JPA
● JPA has a difficult to use static query-model
● Verbose property paths
● Operations via builder object
● Inverse order: “equals property value” vs.
“property equals value”
● Broken flow
30. Criteria example
// All possible pairs of single males and females
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> men = query.from( Person.class );
Root<Person> women = query.from( Person.class );
Predicate menRestriction = builder.and(
builder.equal( men.get( Person_.gender ), Gender.MALE ),
builder.equal( men.get( Person_.relationshipStatus ),
RelationshipStatus.SINGLE )
);
Predicate womenRestriction = builder.and(
builder.equal( women.get( Person_.gender ), Gender.FEMALE ),
builder.equal( women.get( Person_.relationshipStatus ),
RelationshipStatus.SINGLE )
);
query.where( builder.and( menRestriction, womenRestriction ) );
31. Querydsl example
// All possible pairs of single males and females
JPAQuery query = new JPAQuery(entityManager);
QPerson men = new QPerson("men");
QPerson women = new QPerson("women");
query.from(men, women).where(
men.gender.eq(Gender.MALE),
men.relationshipStatus.eq(RelationshipStatus.SINGLE),
women.gender.eq(Gender.FEMALE),
women.relationshipStatus.eq(RelationshipStatus.SINGLE));
32. SQL
● Pretty similar to JPA/Hibernate
● No deep paths over relations though
● No implicit joins
SQLTemplates templates = new MySQLTemplates();
...
SQLQuery query = new SQLQueryImpl(connection,
templates);
query.from(person);
query.innerJoin(parent).on(parent.id.eq(person.parent.id));
● Shortcut for joins with foreign keys
query.innerJoin(person.parentFK, parent);
33. SQL
● Maven plugin for generating query model
● Support for special SQL constructs and extensions
● Databases supported include
● MySQL
● PostgreSQL
● Oracle
● MS SQL Server
● H2
● HSQLDB
● Derby
● SQLite
● CUBRID
34. SQL extensions
● Sub class of AbstractSQLQuery
● e.g. OracleQuery with connectByPrior
● Template expressions
● Direct addition of “flags”
SQLInsertClause insert =
new SQLInsertClause(connection, templates, person);
insert.addFlag(Position.START_OVERRIDE, "replace into ");
37. What does Mysema offer for
Querydsl?
● Free public support
● GitHub Issues
● Querydsl Google Group
● Mysema Blog
● Consulting services
● User support
● Custom extensions and integration
● Training
38. Querydsl support from other
companies
● VMware uses Querydsl in Spring Data for the
following backends
● JPA
● SQL/JDBC
● MongoDB
● Neo4j
● Spring Data is a good option if you want to use
repositories in Spring with Querydsl support