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!

1 comment:

  1. I use QCustomer tableCustomer = QCustomer.customer; as default, the intent becomes perfectly clear.

    ReplyDelete