Spring Data JPA Tutorial Part Five: Querydsl

Easy way sign

The fourth part of my Spring Data JPA tutorial described how you can implement more advanced queries with the JPA criteria API. As you might remember, the goal of the previous part of this tutorial was to implement a search function which returns only such persons whose last name begins with the given search term. This blog entry will describe how you use Querydsl and Spring Data JPA for the same purpose.

Lets get to work and see how you can fulfill this requirement.

Required Steps

The steps needed to implement the given requirement are following:

  • Configuring the Maven integration of QueryDSL
  • Generating the Querydsl query type
  • Implementing the predicate builder class
  • Extending the repository to support Querydsl predicates
  • Using the created repository

Each of these steps is described with more details in following Sections.

Configuring the Maven Integration of Querydsl

The configuration of the maven integration of Querydsl consists two smaller phases:

First, you need to add the Querydsl dependencies to your pom.xml file. The needed dependencies are:

  • Querydsl core which provides the core functions of Querydsl.
  • Querydsl APT integration which provides support for the APT based code generation.
  • Querydsl JPA which provides support for the JPA annotations.

The relevant part of the pom.xml is given in following:

<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-core</artifactId>
    <version>2.3.2</version>
</dependency>

<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>2.3.2</version>
</dependency>

<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>2.3.2</version>
</dependency>

Querydsl uses the Annotation Processing Tool of Java 6 for code generation. Thus, the next phase is to add the the configuration of the Maven APT plugin to the plugins section of your pom.xml file. The relevant part of the pom.xml looks following:

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>maven-apt-plugin</artifactId>
    <version>1.0.2</version>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <!-- Specifies the directory in which the query types are generated -->
                <outputDirectory>target/generated-sources</outputDirectory>
                <!-- States that the APT code generator should look for JPA annotations -->
                <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

Generating the Querydsl Query Type

The next step is to generate the Querydsl query type which is used to construct queries with Querydsl. All you have to do is to build your project and the query type is generated under the target/generated-sources directory. If you open this directory, you should notice that the query type class is created in the same package than the Person class. Since the name of the model class is called Person, the name of the Querydsl query type is QPerson.

NOTE: Remember to add the target/generated-sources directory as a source directory for your project before moving forward (Check the documentation of your IDE for more details about this).

Implementing the Predicate Builder Class

In my previous part of this tutorial, I used a builder class with static methods to build the actual Specification instances. I am following the same principle when building the Querydsl predicates by using the generated QPerson query type. The source code of my predicate builder class is given in following:

import com.mysema.query.types.Predicate;
import net.petrikainulainen.spring.datajpa.model.QPerson;

/**
 * A class which is used to create Querydsl predicates.
 * @author Petri Kainulainen
 */

public class PersonPredicates {

    public static Predicate lastNameIsLike(final String searchTerm) {
        QPerson person = QPerson.person;
        return person.lastName.startsWithIgnoreCase(searchTerm);
    }
}

At first I had no idea how my builder class could be tested, because the information about unit testing Querydsl is kind of hard to find. Luckily I was able to find a google groups thread, which discusses about this matter. After reading that thread I decided that testing the return value of toString() method of the created predicate is enough because this scenario is quite simple. The source code of my unit test is given in following:

import com.mysema.query.types.Predicate;
import org.junit.Test;

public class PersonPredicatesTest {
   
    private static final String SEARCH_TERM = "Foo";
    private static final String EXPECTED_PREDICATE_STRING = "startsWithIgnoreCase(person.lastName,Foo)";

    @Test
    public void lastNameLike() {
        Predicate predicate = PersonPredicates.lastNameIsLike(SEARCH_TERM);
        String predicateAsString = predicate.toString();
        assertEquals(EXPECTED_PREDICATE_STRING, predicateAsString);
    }
}

If you have read my blog entry about using the JPA criteria API with Spring Data JPA, you might remember that the unit test of my lastNameLike() method was rather long and looked a bit messy. The problem is that mocking the JPA criteria API becomes a lot more complex when you are building more complex queries. This means that writing pure unit tests for your code takes longer and longer.

My unit test for the Querydsl implementation of the same method is the exact opposite. It is short and looks quite clean. And more importantly, it is much faster to write. This means that you can concentrate on adding value to your application instead of verifying its behavior.

Extending the Repository to Support Querydsl Predicates

Extending the repository to support Querydsl predicates is quite straightforward process. All you have to do is to extend the QueryDslPredicateExecutor interface. This gives you access to findAll(Predicate predicate) method. This returns all entities fulfilling the search conditions which are specified by the predicate. The source code of my PersonRepository interface is given in following:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;

/**
 * Specifies methods used to obtain and modify person related information
 * which is stored in the database.
 * @author Petri Kainulainen
 */

public interface PersonRepository extends JpaRepository<Person, Long>, QueryDslPredicateExecutor<Person> {

}

Using the Created Repository

The last step is to implement the service class which uses the created predicate builder and the repository. The PersonService interface contains a method search(String searchTerm) which returns a list of persons matching with the given search term. The relevant part of the PersonService interface is given in following:

/**
 * Declares methods used to obtain and modify person information.
 * @author Petri Kainulainen
 */

public interface PersonService {

    /**
     * Searches persons by using the given search term as a parameter.
     * @param searchTerm
     * @return  A list of persons whose last name begins with the given search term. If no persons is found, this method
     *          returns an empty list. This search is case insensitive.
     */

    public List<Person> search(String searchTerm);
}

Implementing the search method is pretty straightforward. My implementation uses the PersonPredicates class to obtain the Querydsl predicate and passes the received predicate forward to the PersonRepository. Since the findAll() method returns Iterable instead of List, I had to add an extra method which converts the returned Iterable into a List. The source code of the search(String searchTerm) method is given in following:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import static net.petrikainulainen.spring.datajpa.repository.PersonPredicates.lastNameIsLike;

/**
 * This implementation of the PersonService interface communicates with
 * the database by using a Spring Data JPA repository.
 * @author Petri Kainulainen
 */

@Service
public class RepositoryPersonService implements PersonService {
   
    private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryPersonService.class);
   
    @Resource
    private PersonRepository personRepository;

    @Transactional(readOnly = true)
    @Override
    public List<Person> search(String searchTerm) {
        LOGGER.debug("Searching persons with search term: " + searchTerm);

        //Passes the specification created by PersonPredicates class to the repository.
        Iterable<Person> persons = personRepository.findAll(lastNameIsLike(searchTerm));
        return constructList(persons);
    }
   
    private List<Person> constructList(Iterable<Person> persons) {
        List<Person> list = new ArrayList<Person>();
        for (Person person: persons) {
            list.add(person);
        }
        return list;
    }
}

The same architectural remarks which I made in the previous part of my Spring Data JPA tutorial are also valid in this case. However, the end result looks pretty clean and simple. The only thing which is left, is to write a unit test for the search method. The source code of the unit test is given in following:

import com.mysema.query.types.Predicate;
import org.junit.Before;
import org.junit.Test;

import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.*;

public class RepositoryPersonServiceTest {

    private static final String SEARCH_TERM = "foo";
   
    private RepositoryPersonService personService;

    private PersonRepository personRepositoryMock;

    @Before
    public void setUp() {
        personService = new RepositoryPersonService();

        personRepositoryMock = mock(PersonRepository.class);
        personService.setPersonRepository(personRepositoryMock);
    }

    @Test
    public void search() {
        List<Person> expected = new ArrayList<Person>();
        when(personRepositoryMock.findAll(any(Predicate.class))).thenReturn(expected);
       
        List<Person> actual = personService.search(SEARCH_TERM);
       
        verify(personRepositoryMock, times(1)).findAll(any(Predicate.class));
        verifyNoMoreInteractions(personRepositoryMock);
       
        assertEquals(expected, actual);
    }
}

What is Next?

I have now demonstrated to you how you can build advanced queries with Spring Data JPA and Querydsl. As always, the example application described in this blog entry is available at Github. The next part of my tutorial describes the sorting capabilities of Spring Data JPA.

About the Author

Petri Kainulainen is passionate about software development and continuous improvement. He is specialized in software development with the Spring Framework and is the author of Spring Data book.

About Petri Kainulainen →

Related Posts

{ 24 comments… add one }

  • neo April 14, 2012 at 3:41 pm

    hi,petri,why in code didn’t find QPerson class

    Reply
    • Petri April 14, 2012 at 3:56 pm

      Hi Neo,

      Did you remember to generate the Querydsl query type and add the target/generated-sources directory as a source directory for your project?

      Reply
    • Manolosoft September 18, 2012 at 5:34 pm

      Hi neo, in the root project right click, in the maven option click in maven-generate-sources after process go to the folder target/generated-sources/your package, in this folder you find the class QPerson.java…

      Greetings.

      Petri, Thanks you, is the best of the best in tutorials, You helped me a lot.

      PD: Sorry for my bad english… Im from Colombia… xD

      Reply
  • neo April 15, 2012 at 6:56 am

    Yes, got it, but don’t understand why. Thank you very much!

    Reply
  • Tiago April 19, 2012 at 12:30 pm

    Hi Petry, nice tutorial!

    I have one question concerning this aproach:

    Suppose I declared some methods that will be parsed into queries by Spring Data JPA in the interface that extends both JpaRepository and QueryDslPredicateExecutor (PersonRepository). “findByFirstname” for instance.

    In the service class (RepositoryPersonService) I implement the complex queries using QueryDsl.

    In the classes that use the service I want to call the simple methods (parsed by JPA) and also the complex methods (QueryDsl). For that I’d have to inject both repositories in my classes? One of type “PersonRepository” and other of type “PersonService”.

    I don’t think it’s a good idea to have two injections that has the same objective. A solution would be copping every relevant method signature to the “PersonService” interface, and implement then at the “RepositoryPersonService” class, making use of an injected “PersonRepository”. But it seems to me too much ctrl+c ctrl+v. Do you know a better approch?

    Reply
    • Petri April 19, 2012 at 7:31 pm

      Hi Tiago,

      Thank you for your comment. You made a good point concerning the problems related to a situation where the PersonRepository is injected to the RepositoryPersonService class. It is indeed true that some methods of the PersonRepositoryService class are just delegating the method call forward to person repository.

      This sucks, but I still prefer this approach because this way I can use the service layer as transaction boundary and provide a decent separation between the controller “logic” and the application “logic”.

      Adam Biem has some interesting ideas concerning the architecture of Java EE applications. He actually provides a solution for the problem you mentioned. Unfortunately, you cannot use his solution if you are using Spring MVC. If you are interested, you should definitely check out his blog: http://www.adam-bien.com/roller/abien/

      Reply
  • Thomas Struntz October 26, 2012 at 9:32 am

    Hi Petri,
    thanks for your tutorials. I have a question regarding querying with querydsl. Is it possible to create a predicate that contains (multiple) Joins?

    I have a many-to-many relationship with a link table. That link table contains an additional field and is also represented by a Entity class.

    I can create findBy methods that span tables. However my actual comparison is not just String or numbers but an actual function call, eg in SQL: where myFunction(:param1, :param2) = true.

    Is his possible?

    Reply
    • Timo Westkämper October 26, 2012 at 10:04 pm

      Hi Thomas.

      Is your question how to create multiple joins in Spring Data with Querydsl or directly in a Querydsl query?

      You can also use custom expressions in Querydsl, but you need to register your functions in some way on the JPA provider level.

      Br,
      Timo Westkämper

      Reply
  • Eric B November 14, 2012 at 6:09 pm

    Hi Petri,

    I’m using Eclipse 3.7 with m2e 1.1. I tried loading your code and it complains that the maven-apt-plugin lifecycle is not covered. I know I can manually generate the sources using maven generate-sources everytime something changes, but that is a bit of a nuissance. I tried instaling the m2e-querydsl plugin (https://github.com/ilx/m2e-querydsl) and that removed the error in the pom, but did nothing to generate the sources in the outputDirectory nor add the generated-sources to the Deployment Assembly.

    Are you aware of a functional m2e connector that works with querydsl? I am hoping that you encountered some while writing your blog and/or book.

    Thanks,

    Eric

    Reply
    • Petri November 14, 2012 at 7:31 pm

      Hi Eric,

      Unfortunately the only advice that I was able to found was to generate the Querydsl query types by running the mvn generate-sources command at command prompt every time when something changes. This is indeed really irritating for Eclipse users and this is one drawback of Querydsl (at least at the moment).

      Reply
  • Eric B November 15, 2012 at 7:12 pm

    Hi Petri,

    After a lot of stuggling and searching, I did find the following. Upgrading to m2e 1.2 and using the following in my pom.xml worked to compile your code. Also enables auto-regen of querydsl classes:

    
    <plugin>
        <!-- Requires mysema m2e plugin (http://ilx.github.com/m2e-querydsl/repository/0.0.5/) -->
        <groupId>com.mysema.maven</groupId>
        <artifactId>maven-apt-plugin</artifactId>
        <version>1.0.4</version>
        <executions>
            <execution>
                <goals>
                    <goal>process</goal>
                </goals>
                <configuration>
                    <logOnlyOnError>true</logOnlyOnError>
                    <outputDirectory>target/generated-sources/apt</outputDirectory>
                    <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
                </configuration>
            </execution>
        </executions>
        <dependencies>
            <dependency>
                <groupId>com.mysema.querydsl</groupId>
                <artifactId>querydsl-apt</artifactId>
                <version>${querydsl.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysema.querydsl</groupId>
                <artifactId>querydsl-jpa</artifactId>
                <classifier>apt</classifier>
                <version>${querydsl.version}</version>
            </dependency>
        </dependencies>
    </plugin>
    <!-- right now this seems needed -->
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
            <execution>
                <id>add-source</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>add-source</goal>
                </goals>
                <configuration>
                    <sources>
                        <source>target/generated-sources/apt</source>
                    </sources>
                </configuration>
            </execution>
        </executions>
    </plugin>
    
    

    This also required the m2e connector for build-helper-maven-plugin which was easily found via the m2e catalog.

    According to the querydsl plugin site (https://github.com/mysema/maven-apt-plugin/issues/9), the apt plugin is supposed to work without the querydsl connector/plugin, but I have been unable to get it to work without that plugin.

    Hopefully this info can help you or someone else in the future.

    Thanks,

    Eric

    Reply
  • hari November 25, 2012 at 6:07 pm

    Hi Petri,
    Can you suggest on how to construct query by joining 2 tables?
    Thanks,
    Hari

    Reply
  • SGB November 26, 2012 at 10:42 pm

    Petri,
    Very nice tutorials.
    As someone just starting to use SpringData and QueryDSL, this tutorial is very helpful.

    That said, in my Service impl class, this is not working for me:

    Iterable persons = personRepository.findAll(lastNameIsLike(searchTerm));

    Instead, I am having to do this:

    Iterable persons = personRepository.findAll(PersonPredicates.lastNameIsLike(searchTerm));

    to be able to use the static method lastNameIsLike in the PersonPredicates class.

    Not sure why – I must be making some mistake…

    Thanks again.

    Reply
    • Petri November 26, 2012 at 10:48 pm

      Hi,

      Good to hear that this blog entry was useful to you. About your problem, are you having a compilation error or a runtime error?

      Reply
  • Sean Creedon December 31, 2012 at 7:44 pm

    Hi,

    good tutorial,
    I am looking at passing multiple search terms (multiple predicates) which is fine within java as I can use BooleanExpression and or etc… methods to build a Predicate to pass to a findAll method.

    Where I’m failing to find info on is how to express this on a REST URL. I don’t want to use findByXxxxAndYyyy as the number of properties is large enough to create a lot of these methods.

    Any advice or pointers much appreciated

    Regards
    Sean

    Reply
    • Petri January 1, 2013 at 8:20 pm

      Are you using using Spring Data REST or a “normal” Spring MVC controller?

      If you are using a “normal” Spring MVC controller, you can create a request handler method that handles GET requests and specify the search conditions as optional request attributes (Use @RequestParam annotation and set the value of its required property to false.

      I have not personally tried Spring Data REST but I took a quick look at it and it seems that it can be used to provide automatic CRUD operations as a REST API. It has a support for search as well, but it seems that you have to create your queries by using the query generation by method name strategy. Like you said, this means that you have to create multiple repository methods if you have many optional search parameters. If this is not an option, you can always create a “normal” Spring MVC controller and use the technique described above.

      Reply
      • Sean Creedon January 2, 2013 at 11:27 pm

        Thanks
        I’m using spring MVC controller at the moment, but I’m trying to ensure my URL is such that it aligns with spring data rest, so I’m trying to see what other people think. I’ll do a bit more digging on Spring data REST and post back here.

        Reply
  • Stephane April 25, 2013 at 3:20 pm

    Hi Petri,

    I’m now using your book, it’s quite an easy ride into Spring Data JPA, thank you for that.

    My only road block for now is that I have a multi modules Maven project and that maven-apt-plugin configuration gives an error with a basedir not being found.

    [ERROR] execute error
    java.lang.IllegalStateException: basedir /home/stephane/dev/java/projects/learnintouch/src/main/java does not exist

    I guess there may be a property to set in there, specifying the input directory.

    But I can’t yet find that information.

    Kind Regards,

    Reply
    • Petri April 25, 2013 at 8:50 pm

      Hi Stephane,

      Have you configured the Maven APT plugin in the POM file of the module which contains your domain model classes?

      Reply
  • Francois May 10, 2013 at 1:00 pm

    Hello Petri,

    Being new to Spring (and to java in general), I really enjoy your tutorials, this really help me get forward and learn new skills directly useable!
    I tired this one and I had at first tried the querydsl version 3.1.1, however I always got a nice stack:

    java.lang.NoSuchMethodError: com.mysema.query.jpa.impl.JPAQuery.from([Lcom/mysema/query/types/EntityPath;)Lcom/mysema/query/jpa/JPQLQueryBase;
    ….
    downgrading querydsl to 2.3.2 did the trick.

    I tried reading their documentation for v3.1.1, but I could not find anything that would point me into the correct direction?
    What should be changed in the above example to have it work with querydsl 3.1.1?

    Thanks a lot in advance ;)
    Francois

    Reply

Leave a Comment