2013-03-28

Querydsl 3.0 in a nutshell

Last week we released Querydsl 3.0. The goal of this release was to streamline Querydsl usage. In this post I will highlight a few of the general changes and some JPA/SQL specific ones.

Naming changes

Naming of query and subquery classes has been unified into the following form Query classes:
Module before after
Collections ColQuery (interface) - ColQueryImpl CollQuery
JDO JDOQLQuery (interface) - JDOQLQueryImpl JDOQLQuery (interface) - JDOQuery
SQL SQLQuery (interface) - SQLQueryImpl SQLQuery

No changes in JPA, Mongodb, Lucene and Hibernate Search.

Subquery classes:
Module before after
JPA JPQLSubQuery - JPASubQuery - HibernateSubQuery JPQLSubQuery (interface) - JPASubQuery - HibernateSubQuery
SQL SQLSubQuery SQLSubQuery

https://github.com/mysema/querydsl/issues/186

Tuples as first-class citizens

We encountered the following pattern often in our code
query.list(new QTuple(...))
and decided to change the varargs list method to return a list of tuples instead of object arrays. Thus
List<Tuple> result = query.list(new QTuple(a, b, …)) 
calls can be refactored into
List<Tuple> result = query.list(a, b, …)
The same change has been performed for the other varargs projection methods of the Projectable interface.

https://github.com/mysema/querydsl/issues/296

Unified distinct usage

The following distinct usage has been deprecated
query.listDistinct(a, b, c)
The preferred usage is now
query.distinct().list(a, b, c)
Which is more in line with other modifiers that are applied to the query.

https://github.com/mysema/querydsl/issues/303

Deeper defaults for path initialization

Querydsl metaclasses are now initialized one level further, so now there are less risks of running into issues when deep paths are used. And if you want to be absolute sure that all paths are initialized you can always use the accessor option as documented here.

https://github.com/mysema/querydsl/issues/301

Improved iteration for JPA

The iterate methods in Querydsl JPA query classes have been optimized to utilize Hibernate and EclipseLink specific iteration support. For Hibernate ScrollableResults are used and for EclipseLink Cursors.

https://github.com/mysema/querydsl/issues/346

Batoo JPA support

Batoo JPA is now officially supported by Querydsl.

https://github.com/mysema/querydsl/issues/265

Roo support

The @RooJpaEntity annotation of the Spring Roo project is now officially supported via the com.mysema.query.apt.roo.RooAnnotationProcessor processor. The workaround of using GenericExporter is no longer necessary.

https://github.com/mysema/querydsl/issues/318

with -> on

The with method for Querydsl JPA queries has been replaced with on, to promote a more consistent line of keywords in the query interfaces.

https://github.com/mysema/querydsl/issues/299

Support for order by nulls first

Explicit null ordering is now supported in Querydsl SQL. The supported extensions are
query.orderBy(lastName.asc().nullsFirst())
and
query.orderBy(lastName.asc().nullsLast())     

https://github.com/mysema/querydsl/issues/174

Builder methods for SQLTemplates types

The previous method of customizing SQLTemplates subclasses was a little awkward and involved further subclassing like this
new H2Templates(true){{
    newLineToSingleSpace();
}}
Querydsl 3 promotes now the usage of builder classes for customization
H2Templates.builder()
           .quote()
           .newLineToSingleSpace()
           .build();

https://github.com/mysema/querydsl/issues/355

Conclusion

The full list of changes for the 3.0 release are in 3.0.0, 3.0.0.BETA2 and 3.0.0.BETA1.

The latest stable release of Querydsl is 3.1. It fixes some packaging issues of the 3.0 release: https://groups.google.com/forum/#!topic/querydsl/I562uKQlheE. Usage of the 3.1 release is recommended.

Happy querying!

2012-07-03

Modularizing JavaScript Code with Backbone.js and RequireJS

Lately we have been doing a lot of JavaScript development and have been very happy about the improvements in libraries and frameworks, long gone are the days of seeing JavaScript just as a necessary evil. Unfortunately there is still legacy code lying around even in our codebase.

As an example I present an application that was developed by us: Semantic.hri.fi, it provides a Linked Open Data view of www.aluesarjat.fi statistics. What we will be looking at is the SPARQL search page and mainly its source code.

Even with a quick glance it is easy to spot several issues which are nowadays handled with a different approach:

  • String prototype is monkey patched with a function that is only used once
  • HTML is constructed in code via string concatenation
  • Application is not properly layered due to rendering happening in back-end data fetches
  • Hard to tell which functions are managing which area of the screen
  • File is very long
  • Global namespace is polluted with several variables

Now that we have identified some of the problem spots, how would we fix them?

String prototype is monkey patched with a function that is only used once

Monkey patching causes compatibility issues and thus is not very sensible. Luckily an easy fix is just to use a function that accepts the string as an argument. Another option of course is to use an external library for handling string manipulation, such as Underscore.string.

HTML is constructed in code via string concatenation

Instead of manually constructing HTML like this:

//...
html.push("<thead><tr>");
for (var i = 0; i < vars.length; i++){
  html.push("<th>" + vars[i] + "</th>");
}       
html.push("</tr></thead>");
// ..

I would use a templating library such as Handlebars. With Handlebars we would have a template declared in its own file like this:

...
<thead>
  <tr>
    {{#each vars}}
    <th>{{this}}<th>
    {{/each}}
  </tr>
</thead>
...

And the code to populate it with:

var template = Handlebars.compile(tableTemplate);
$('#results').html(template({vars: vars}));

Application is not properly layered due to rendering happening in back-end data fetches

Instead of doing DOM manipulation inside an AJAX success callback like this:

success: function(data){
  var defaultNamespaces = [];
  var bindings = data.results.bindings;
  for (var i = 0; i < bindings.length; i++){
    var binding = bindings[i];
    namespaces[binding["prefix"].value] = binding["ns"].value;
    defaultNamespaces.push("PREFIX ", binding["prefix"].value, ": <", binding["ns"].value, "> </br>\n");
  }
  
  $("#namespaces").html(defaultNamespaces.join(""));
  
  init();
}

I would rather just fire an event to inform anyone who is interested that we are done loading. For this we use a common event aggregator called vent, which is just a JavaScript object with Backbone.Events mixed in. With vent in use I would change the above to something like this:

success: function(data) {
  vent.trigger('query:success', data.results.bindings);
}

This enables the success function to only care about retrieving the data. How the data is processed later on is left to the subscriber(s).

Hard to tell which functions are managing which area of the screen

Looking at the code I am having a really hard time understanding which functions are responsible for managing which areas of the screen, they should definitely be somehow bundled. A great way to do this is with Backbone.Views.

I would probably first create a SparqlView which is composed of SearchView (one on the left) and SavedQueriesView. After that I would probably split SearchView into two separate views QueryView and ResultsView. Below is a pseudocode example of the structure:

SparqlView
  SearchView
    QueryView
    ResultsView
  SavedQueriesView

Backbone.Views would probably be used for smaller elements as well, but these changes alone would give us a better understanding of the structure, plus the benefit of all the goodies Backbone.View provides.

File is very long

This can be of course fixed by splitting the file into separate files and then loading them separately. Luckily for us, we have already split the code into separate Backbone entities so moving them into their own files is really easy:

ViewFilename
SparqlViewjs/views/sparql.js
SearchViewjs/views/sparql/search.js
QueryViewjs/views/sparql/search/query.js
ResultsViewjs/views/sparql/search/results.js
SavedQueriesViewjs/views/sparql/saved_queries.js

When the subview has only one parent I find it a good practice to put the subview into a subdirectory with the name of their parent.

Global namespace is polluted with several variables

We have split the code into separate files, but have not really done anything about polluting the global namespace and our HTML file is full of script sources. I would bring in RequireJS to sort out this mess.

With RequireJS we declare only our application entry point in the HTML:

<script data-main="main" src="js/libs/require/require.js"></script>

data-main specifies which file is used as the entry point, in our case it is main.js and its contents is something like the following:

require(['app'], function(App) {
  App.initialize();
});

require is a function that takes an array as its first argument and a function as its second. The values in the array are RequireJS modules which are fed into the function as arguments. So in this case the argument App is module app.js. Inside the function we call App's method initialize, let's have a look at app.js:

define(['js/libs/jquery/jquery', 'js/views/sparql.js'],
  function($, SparqlView) {
  var initialize = function() {
    new SparqlView({el: $('#sparql')});
  }

  return {initialize: initialize};
});

Here we have defined a RequireJS module that has two dependencies: jQuery and SparqlView and we only expose a hash containing one function, initialize. When called, initialize creates a new SparqlView, SparqlView is defined as a RequireJS module as all of its subviews.

We now hopefully have a clear view on how to take old JavaScript code to the present.

2012-06-21

Form Data Extraction with Backbone.js, Underscore.js and jQuery

Our weapon of choice for building single-page web applications is Backbone.js. Unlike a few of its competitors, such as Ember.js, Knockout, Backbone does not support model binding out of the box. There are a some libraries that add model binding support to Backbone, probably first on the block was Backbone.ModelBinding which has now been discontinued, Backbone.ModelBinder and Backbone Bindings.

Even though these libraries make sense in some cases, we have noticed that usually when starting out, Backbone's default tools (Backbone itself, Underscore.js and jQuery) are enough to get the ball rolling, as demonstrated below.

HTML

<form>
  Name: <input type="text" name="name"/>
  Age: <input type="text" name="age"/>
  <input type="submit" value="Submit"></input>
</form>

JavaScript

var UserForm = Backbone.View.extend({
  events: {'submit': 'save'},

  initialize: function() {
    _.bindAll(this, 'save');
  },
  
  save: function() {
    var arr = this.$el.serializeArray();
    var data = _(arr).reduce(function(acc, field) {
      acc[field.name] = field.value;
      return acc;
    }, {});
    this.model.save(data);
    return false;
  }
});

var userForm = new UserForm({el: this.$('form'), model: new User()});

To understand the actual data extraction I will explain serializeArray and reduce.

serializeArray is a jQuery method that takes a form and constructs an array of all the input elements that have a name attribute. The resulting array is in the following form:

[{name: "name", value: "John Smith"}, ...]

An array of maps is not quite something we are looking for, so we have to do another transformation.

reduce is used to build up a new value from the array with an original seed value (empty map) and a function. The function's first argument acc is the return value of the previous function call and the second argument field is the current element in the array. Now we have something that can be passed to Backbone.Model.save:

{name: "John Smith", age: 34}

2012-03-09

Scalagen - Java to Scala Conversion

Scalagen was born out of the idea that we would like to port some of our projects from Java to Scala source code. Instead of porting all the code manually a tool could be used to do most of the bulk work. We did some initial experiments with Jatran, but found it lacking.

After that we investigated some options to write such a tool ourselves. First we needed a parser for Java sources to turn into abstract syntax trees. We decided to use the Javaparser framework since it supports Java 5 source and had an easy to use API.

Initial Architecture

Then work began on Scalagen. The initial parts were written in Java, such as a visitor implementation for the javaparser which prints out the tree as Scala syntax. After that had been done this visitor was used to convert the visitor class from Java to Scala. After that the implementation of Scalagen continued to be fully Scala-based.

After that we wrote visitor implementations which did transformations on the AST. Needed transformations were to extract static members into companion objects and to change various control statements into a more Scala-esque form. At first the visitor would just mutate the original AST from one state to another. This proved to cause problems with Scala's immutable collection types so we rewrote the transforming visitors to always return new detached AST versions.

Now the initial architecture was set up and further transformations could be written. After a while we had an impressive list of transformations which could be easily used via both their class and object variants:

object Primitives extends Primitives


class Primitives extends UnitTransformerBase {

...

}

Wrapping the Parser

Then we began to adapt the javaparser API to be more easy to use. We declared short type aliases in a singleton object to strip off redundant suffixes such as Stmt and Expr:

type Annotation = AnnotationExpr 

type AnnotationDecl = AnnotationDeclaration 

type AnnotationMember = AnnotationMemberDeclaration

type Assign = AssignExpr  

type Binary = BinaryExpr   

type Block = BlockStmt 

type BodyDecl = BodyDeclaration

Then we began to write deconstructors to make AST pattern matching more concise:

object FieldAccess {
  def unapply(f: FieldAccess) = Some(f.getScope, f.getField)
} 

object For {
  def unapply(f: For) = Some(toScalaList(f.getInit), f.getCompare, 
    toScalaList(f.getUpdate), extract(f.getBody))
}

object Foreach {
  def unapply(f: Foreach) = Some(f.getVariable, f.getIterable, 
    extract(f.getBody))
}

object MethodCall {
  def unapply(m: MethodCall) = Some(m.getScope, m.getName, 
    toScalaList(m.getArgs))
}

And something more complex which provides both a new-less constructor via apply and a deconstructor via unapply.

object VariableDeclaration {
  def apply(mod: Int, name: String, t: Type): VariableDeclaration = {
     val variable = new VariableDeclarator(new VariableDeclaratorId(name))
     new VariableDeclaration(mod, t, variable :: Nil)
   }
  def unapply(v: VariableDeclaration) = 
    Some(v.getType, toScalaList(v.getVars))
}

The singleton names matched the type aliases so we had a very Scala-like meta-layer on top of the javaparser AST classes.

Here is a fairly complex example of how the pattern matching could be used in the transforming visitors.

override def visit(nn: Foreach, arg: CompilationUnit): Node = {
  val n = super.visit(nn, arg).asInstanceOf[Foreach]
  n match {
    case Foreach(
      VariableDeclaration(t, v :: Nil),
      MethodCall(scope, "entrySet", Nil), body) => {
        val vid = v.getId.toString
        new Foreach(
          VariableDeclaration(0, "(key, value)", Type.Object),
          scope, n.getBody.accept(toKeyAndValue, vid).asInstanceOf[Block])    
    }
    case _ => n
  }   
}

This looks still quite cryptic if you are not familiar with the AST structure of javaparser, but for the ones familiar with structure of the AST, this is a fairly intuitive way to match AST patterns.

Packaging

Scalagen provides direct Maven support via a plugin. You can use it directly via the command line like this

mvn com.mysema.scalagen:scalagen-maven-plugin:0.1.3:main \
  -DtargetFolder=target/scala

and for test sources

mvn com.mysema.scalagen:scalagen-maven-plugin:0.1.3:test \
  -DtargetFolder=target/scala

Here is the snippet for an explicit configuration in a POM:

<plugin>
 <groupId>com.mysema.scalagen</groupId>
 <artifactId>scalagen-maven-plugin</artifactId>
 <version>0.1.3</version>
</plugin>

To convert main sources run

mvn scalagen:main

and to convert test sources run

mvn scalagen:test

The conversion results are to be seen as a starting point for the Java to Scala conversion. Some elements are not transformed correctly for various reasons and will need manual intervention.

Finally

Scalagen is an experimental effort and has still lots of rough edges, but we are open to improvement suggestions and stylistic changes.

2011-11-01

On Ontology creation

Last week I held a presentation at the Kirjastoverkkopäivät event in Helsinki and talked about the stages of Ontology creation. Instead of posting the slides here I decided to elaborate more on the topics of the presentation.
The need for ontology creation arises often from having to agree on a formal terminology. Formal terminologies are important to share understanding and to reduce ambiguity. And to create a formal terminology a conceptual framework is needed to build on. This is where ontologies come in.
An ontology is a formal representation of knowledge as a set of concepts within a domain, and the relationships between those concepts.

Ontology standards

To be able to share ontologies and to reuse other ontologies in fragments or full form a common agreement on the building blocks of ontologies and their meaning is needed. The Semantic Web effort of the World Web Foundation has been dealing with this goal and has published several standards to tackle the problem.
The most relevant ontology standards today are RDF Schema, OWL 1.0 & 2.0 and SKOS.
  • RDF Schema example
    published in 1998, standardized in 2004
  • OWL example
    • 1.0 (2004)
      divided into the dialects Light, DL (Description Logic) and Full, extends RDF Schema
    • 2.0 (2009)
      divided into the profiles EL, QL and RL
  • SKOS example
    ontology for vocabularies and thesauri
RDF Schema and OWL are ontology frameworks which provide a type system of classes, properties and instances and SKOS can be seen as a ontology which defines concept types and relations between concepts.
The following sections will show examples to illustrate the differences and similarities of the presented standards.

RDF Schema

The Wine ontology is a popular RDF Schema example as it presents in compact form a lot of RDF schema building blocks. The boxes of the images present classes, predicates and instances while the lines present properties. The ElyseZinfandel box for example is connected to the Zinfandel box via the rdf:type relation which means the resource ElyseZinfandel has the type Zinfandel. It is also connected to the Elyse box, but via the hasMaker relation, and to the Red box, via the hasColor relation.
The predicates rdfs:subClassOf and rdfs:subPropertyOf are used to declare subclass and subproperty relations. rdfs:domain declares the subject range of a property, the type this predicate applies to while rdfs:range declares the object range of a property, i.e. the value range of the property.

OWL

The BBC Programmes ontology defines various concepts of the Media domain. This excerpt highlights the the owl:disjointWith relation of the OWL format.The declaration ProgrammeItem owl:disjointWith Brand for example declares that ProgrammeItem instances cannot be Brand instances and vice versa. Both ProgrammeItem and Brand are sub classes of the Programme class.

SKOS

The magazine ontology of ONKI defines concepts to be used for categorization of magazines. For example Living has the Antiques subcategory while Antiques has the subcategory Antique stores. The skos:broader relation is comparable with the rdfs:subClassOf relation, but while rdfs:subClassOf is transitive, skos:broader isn't. For transitive relations the skos:broaderTransitive relation needs to be used.
For a more detailed presentation of the W3C Ontology and Vocabulary formats visit here.

Creation of your own ontology

Sometimes the available ontologies don't cover your problem domain or define semantics that are not compatible with your needs. Defining your own ontology is then a valid option.
Ontology creation begins of with informal sketching of the concepts of the problem domain. Paper and pen, mind maps or any other sketching tool can be used for this. After that the main modeling dimensions should be defined. Do the concepts define a type or categorization system? Are subclass or subconcept relations the dominating relations or maybe temporal ones or something else? The answers to these questions will guide you to the selection of the appropriate modeling standard. As mentioned before RDF Schema and OWL should be picked to model type systems and SKOS for categorizations.

Relation of your own ontology to others

Sometimes most of your problem domain has already been formalized appropriately in other ontologies. In such a case you can link your own ontology to these external ones. Your own classes, predicates and concepts can be specializations and generalizations of appropriate external entities. Another common way is to define equivalence between entities via the the owl:sameAs relation.
The owl:sameAs relation is used much in the Linked Open Data movement to connect heterogenous datasets.

Where to go next?

Maintenance, further development and distribution need to be taken into account as well. Having the facilities in house to tackle these aspects is a great benefit. Otherwise probably a public host for ontologies such as ONKI should be picked.

ONKI

ONKI is a central ontology host of the finnish Semantic Web scene and is maintained by the SeCo team of the Aalto University. ONKI hosts 72 ontologies and vocabularies altogether: 47 OWL ontologies, 2 RDF Schema ontologies and 22 SKOS vocabularies. Most of these are freely available with a liberal license. The SeCo provides support for the ONKI service via email and notifices via Twitter of service changes and problems.

Using an ONKI ontology

ONKI provides several tools to use ontologies :
  • ONKI Browser - a faceted browser to explore ontology content
  • SOAP interface - for service integration
  • ONKI Selector widget - a JavaScript widget for web application integration
  • Downloads - in RDF/XML form to use the data in your own applications

ONKI Browser

This screenshot shows a part of the ONKI Browser tool, a faceted search for the ONKI content. The ONKI Browser provides free text search for ontologies and concepts and provides a navigatable tree visualization of the concept inheritance.

SOAP interface

The following methods are available in the ONKI SOAP interface:
  • getAvailableLanguages - languages used in concept names in an ontology.
  • getAvailableTypeUris - supported concept type URIs of an ontology.
  • search - searching for ontological concepts.
  • getLabel - fetching a label for the concept with a given URI.
  • expandQuery - expanding concept(s) with given URI(s) (e.g. to subconcepts) for queries.
  • getProperties - fetching properties for the concept with a given URI.
  • getConceptTree - fetching concept hierarchy for the concept with a given URI.
A REST/JSON interface is in beta phase, but the interface is still subject to change.

ONKI Selector wdiget

The ONKI Selector widget is a JavaScript script which can easily be embedded into modern web applications. It provides autocomplete based selection of ontology concepts. It is a fairly easy integration, but provides only label and URI of the selected concept, super concepts need to be fetched separately.
The displayed form fields offer the selection of a specific ontology to be targeted in the search or all, the search query and the language of the labels.

Using an ONKI ontology in your own service

While ONKI is a general ontology host, most of the ontologies are connected to the central YSO ontology, a general finnish language ontology. The following picture shows the umbrella function of the YSO ontology:
The benefit of this approach is that when using an ONKI ontology you have a rich foundation to start with, the downside is that the amount of concepts is huge, the YSO ontology alone hosts 20.000 concepts. Avoiding clashes and semantic mismatches can be difficult with so many concepts to be taken into account.
As the background of YSO is YSA, a central Finnish language glossary, it should probably be treated more as a vocabulary than ontology.

Conclusion

This article presented an overview of the ontology standards RDF Schema, OWL and SKOS with examples and a small introduction into the toolset of the ONKI service. If you have experiences with the ONKI service you'd like to share please comment on this post.

2011-08-26

Accessing Helsinki region statistics from your webapp

In this post I'd like to describe how the statistical data from the Semantic.hfi.fi service can be accessed from your web application via the demo app Areastats. Semantic.hri.fi offers several JSON/P web services which can directly be used from any website.

Areastats displays statistics when accessed from within the Finnish capital region. It uses internally the

  • HTML 5 Geolocation API to obtain the access location as WGS 85 coordinates,
  • a simple webservice to convert the coordinates into a code for the area the location resides in,
  • the Semantic.hri.fi Faceted Search API to access statistics for this area and
  • the jQuery API for DOM manipulation and Ajax

The geo-location-js API is used as an adapter for the incompatible Geolocation APIs used in various browsers. It provides a convenient access point to register callbacks for action when the location could be obtained and when not:

if (geo_position_js.init()) {
    geo_position_js.getCurrentPosition(geo_success, geo_error);
}

The geo_success callback then retrieves the area code from a web service via Ajax and uses the fetch_stats function to process the result:

function geo_success(p) {
  $.ajax({
    url: "polygon", 
    data: { "latitude": p.coords.latitude, "longitude": p.coords.longitude}, 
    dataType: "json", 
    success: fetch_stats,
    error: geo_error
  });
}

The fetch_stats then makes an Ajax call to obtain the results:

function fetch_stats(d) {
  var alue = "alue:_" + d.KUNTA + "_" + d.PIEN + "_" + d.Nimi;
 
  $.ajax({
    url: "http://semantic.hri.fi/search",
    data: { 
      "include": ["items"], "limit":50, "offset":0, 
      "value": [
        alue, 
        "vuosi:_2010", 
        "yksikkö:Henkilöä", 
        "dataset:A025_HKI_Vakiluku1962"] },
    dataType: "jsonp",
    success: fetch_stats_success,
    error: fetch_stats_error
  });
  $("#location").html(d.Nimi);
}

The query defines that we are interested in statistical items from the year 2010, with the unit persons (Henkilöä) and from the dataset dataset:A025_HKI_Vakiluku1962. For details on how to use the service of Semantic.hri.fi, please read the embedded documentation.

The fetch_stats_success callback then has the final results and draws a pie chart based on them:

function fetch_stats_success(d) {
  var age_index = -1;
  for (var i = 0; i < d.headers.length; i++) {
    if (d.headers[i] == "dimension:Ikäryhmä") {
      age_index = i;
    }
  }

  for (var i = 0; i < d.items.length; i++) {
    var key = d.items[i].values[age_index];
    key = key.substring(9);
    $("#"+key).html(d.items[i].value);
  }

  drawpie();
  $("#stats").show();
}

The fetch_stats_success function iterates the headers of the result to find the index of the column we are interested in and the iterates over the result items and maps the column data into the DOM of the local HTML document. After that the drawpie function is invoked and the stats can be shown.

This demo is related to the Helsinki Region Infoshare project, which aims to provide data of the Finnish capital region in Linked Open Data form.

Feel free to contact us if you need more info on the demo or the Semantic.hri.fi services.

2011-08-12

Querydsl in the wild

From time to time we observe how the Querydsl framework is used outside of Mysema to find out how Querydsl meets the needs of our user base. In this blog post I'd like to share some Querydsl usage patterns we found.

Expression factory class

As an alternative to Expression construction based on the core primitives reocurring constraints can be bundled as factory methods grouped by the used root types.

This example shows Expression factory methods for the User domain type

package org.springframework.data.jpa.example.repository;

import org.joda.time.LocalDate;
import org.springframework.data.jpa.example.domain.QUser;

import com.mysema.query.types.expr.BooleanExpression;

class UserExpressions {

    public static BooleanExpression goldMember() {
        return adult().and(createdAtleastOneYearAgo());
    }
    
    public static BooleanExpression adult() {
        return QUser.user.age.goe(18);
    }
    
    public static BooleanExpression createdAtleastOneYearAgo() {
        return QUser.user.creationDate.loe(new LocalDate().minusYears(1));
    }
    
    public static BooleanExpression differentFrontThanLastName() {
        return QUser.user.firstname.eq(QUser.user.lastname).not();
    }
    
    public static BooleanExpression usernameLongerThanSix() {
        return QUser.user.username.length().gt(6);
    }
    
}

Source: GitHub

$ as variable name for default entity path

In cases where Querydsl is used in combination with the Repository pattern it can be convenient to use a general variable name for the default entity path to be used. The dollar-sign is a valid candidate for this:

package reevent.dao;

import com.mysema.query.jpa.JPQLQuery;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import reevent.domain.QUser;
import reevent.domain.User;

@Transactional
@Repository
public class UserDaoJpa extends EntityDaoBase<User> implements UserDao {
    private static final QUser $ = QUser.user;

    @Override
    public User findByUsername(String username) {
        return queryByUsername(username).uniqueResult($);
    }

    @Override
    public boolean usernameExists(String username) {
        return queryByUsername(username).exists();
    }

    private JPQLQuery queryByUsername(String username) {
        return query().from($).where($.username.eq(username));
    }
}

Source: GitHub

Drawbacks with this approach are that some code inspection tools might complain about the unconventional variable name usage and that the dollar-sign is also used by the Querydsl Alias-functionality.

Hamcrest integration

Querydsl expressions can also be used for other tasks beside querying. A fine example is Hamcrest integration:

package org.hamcrest;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.PathMatcher.valueOf;
import static org.junit.Assert.assertThat;

import org.hamcrest.domain.Address;
import org.hamcrest.domain.Customer;
import org.hamcrest.domain.QCustomer;
import org.junit.Test;

public class PathMatcherTest {

    @Test
    public void testPathEvaluate() {
        Customer customer = new Customer();
        Address address = new Address();
        address.setCity("bla");
        address.setStreet("blaway 142");
        customer.setAddress(address);

        assertThat(customer, 
            valueOf(QCustomer.customer.address.street, 
            equalTo("blaway 142")));
    }
    
}

Hamcrest matchers can be used in unit tests for complex assertion construction.

Jeroen van Schagen, the author of the Hamcrest example has bundled his Querydsl extensions into the querydsl-support module.

Supplying filter criteria to general find methods

This pattern is extensively used in the Spring Data repositories. Here is a simple example:

QCustomer customer = QCustomer.customer;
LocalDate today = new LocalDate();
BooleanExpression customerHasBirthday = customer.birthday.eq(today);
BooleanExpression isLongTermCustomer = 
    customer.createdAt.lt(today.minusYears(2));
customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer));

Advanced Spring Data JPA – Specifications and Querydsl

If you have innovative Querydsl usage yourself, please share it with us!