Querydsl overview 2014

timowestkamper 5,682 views 34 slides May 13, 2014
Slide 1
Slide 1 of 34
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34

About This Presentation

Querydsl presentation


Slide Content

Querydsl

Most popular querying tool for Java
Timo Westkämper
@timowest
www.querydsl.com

What?
●Querydsl is an easy to use unified 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, Mongodb, SQL, Lucene...

Why?
●Querydsl makes you more productive and your code
less errorprone
●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

How?
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);

is translated into

select person from com.acme.Person person
where person.firstName eq = ?1 and person.lastName = ?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

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();

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

Querydsl to the rescue!
●Create your variables
QPerson.person // default variable
new QPerson("myPerson") // custom variable

●Create your query
JPAQuery, HibernateQuery, SQLQuery etc

●Populate your query
from, where, groupBy, having, orderBy

●Get the results
count, iterate, list, uniqueResult

Order
// Get persons ordered by last name and first name (desc)
query.from(person)
.orderBy(person.lastName.asc(), person.firstName.desc())
.list(person);

translated into

select person from Person person
order by person.lastname asc, person.firstName desc

Order
// Get persons ordered by women first
query.from(person)
.orderBy(person.gender
.when(Gender.FEMALE).then(0)
.otherwise(1).asc())
.list(person);

translated into

select person from Person person
order by case person.gender = Gender.FEMALE then 0 else 1 end asc

Grouping
// Get person counts grouped by last name
query.from(person)
.groupBy(person.lastName)
.list(person.lastName, person.count());

translated into

select person.lastName, count(person) from Person person
group by person.lastName

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);

translated into

select person from Person person
where person.children.size() = (
select max(parent.children.size()) from Person parent)

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)));

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");
}

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);

Update
// Set firstName of all Does to John
long updatedRowCount =
new JPAUpdateClause(getEntityManager(), person)
.set(person.firstName, "John")
.where(person.lastName.eq("Doe"))
.execute();

translated into

update Person person
set person.firstName = ?1
where person.lastName = ?2

Delete
// Delete all John Does
long updatedRowCount =
new JPADeleteClause(getEntityManager(), person)
.where(person.lastName.eq("Doe"),
person.firstName.eq("John"))
.execute();

translated into

delete Person person
where person.lastName = ?1 and person.firstName = ?2

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

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)

Delegate methods
public class MyQueryExtensions {
@QueryDelegate(Date.class)
public static NumberExpression<Integer> yearAndMonth(DateTimePath<Date> date) {
return date.year().multiply(100).add(date.month());
}
}

causes code generation of

package ext.java.util;
...
public class QDate extends DateTimePath<java.util.Date> {
...
public NumberExpression<Integer> yearAndMonth() {
return MyQueryExtensions.yearAndMonth(this);
}
}

Template expressions
// ilike
query.from(person)
.where(BooleanTemplate.create("{0} ilike {1}”,
person.lastName, ConstantImpl.create("P%")))
.list(person);

translated into

select person from Person person
where person.lastName ilike ?1

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)));
}
}

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

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 ) );

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));

SQL
●Pretty similar to JPA/Hibernate
●No deep paths over relations though
●No implicit joins

SQLTemplates templates = new MySQLTemplates();
...
SQLQuery query = new SQLQuery(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);

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
●Teradata

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 ");

Collections
●Provides querying functionality over collections of
beans with joins, filtering and sorting
●The same metamodel types can be used like for e.g.
JPA and Mongodb

List<User> users = CollQueryFactory.from(user, users)
.where(user.firstName.eq(“Bob”))
.list(user);

JPA/Hibernate Maven
Integration
<build><plugins><plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.0.9</version>
<executions>
<execution>
<goals><goal>process</goal></goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin></plugins></build>

SQL Maven Integration
<build><plugins><plugin>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-maven-plugin</artifactId>
<version>${querydsl.version}</version>
<executions><execution>
<goals><goal>export</goal></goals>
</execution></executions>
<configuration>
<jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver>
<jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl>
<!—- optional elements : namePrefix, jdbcUser, jdbcPassword, schemaPattern, tableNamePattern -->
<packageName>com.myproject.domain</packageName>
<targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
</configuration>
<dependencies><dependency>
<!—- jdbc driver dependency -->
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>${derby.version}</version>
</dependency></dependencies>
</plugin></plugins></build>

What services does Mysema
offer for Querydsl?
●Free public support
●GitHub Issues
●Querydsl Google Group
●Mysema Blog
●Consulting services
●User support
●Custom extensions and integration
●Training

Questions?

Thanks!
Timo Westkämper
@timowest
www.querydsl.com
www.mysema.com
Tags