This is the ninth and the last part of my Spring Data JPA tutorial. Now it is time to take a look of what we have learned, and how we should use it to build better software.

Table of Contents

The contents of my Spring Data JPA tutorial is given in following:

The next step is to take a look of the advantages provided by Spring Data JPA and learn how we can use it in effective manner.

Promises Kept

The goal of the Spring Data JPA project is stated:

Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.

This is a lot to promise. The question is, has Spring Data JPA achieved its goal. As you have learned from my tutorial, Spring Data JPA has following advantages over the “old school” method of building JPA repositories:

  • It provides CRUD capabilities to any domain object without the need of any boilerplate code.
  • It minimizes the amount of source code needed to write custom queries.
  • It offers simple abstractions for performing common tasks like sorting an pagination.

The thing is that implementing these functions have forced the developers to write a lot of boilerplate code in the past. Spring Data JPA changes all this. It minimizes the amount of code needed for implementing repositories.

Making It Work for You

I hate the term best practices because it has a negative effect on continuous improvement. However, I still feel that it is my responsibility to give you some guidance concerning the usage of Spring Data JPA. Here are my five cents about this matter:

Creating Queries

Your goal should be to use the Spring Data JPA to reduce the amount of code you have to write. With this goal in mind, I will you give some guidelines for creating queries with Spring Data JPA:

  • If the query can be build by using the query generation from method name strategy, I think you should use it. However, if the method name will become long and messy, I would consider using the @Query annotation in order to make the source code more readable.
  • Your second option for creating queries should be the @Query annotation and JPQL. This approach ensures that the you will not have to write more code than it is necessary.
  • Use JPA Criteria API or Querydsl only when you have no other options. Remember to extract the query generation logic into separate classes which creates Specification or Predicate objects (Depending on your technology selection).

JPA Criteria API Versus Querydsl

This is a question which should be asked by each developer. The usage of JPA Criteria API has been argued by claiming that you can use it to build type safe queries. Even though this is true, you can achieve the same goal by using the Querydsl. The first round ends in a draw, and we need to look for the answer from a bit deeper.

I will compare these two options in following categories: readability and testability.

Readability

Programs must be written for people to read, and only incidentally for machines to execute

– Abelson and Sussman on Programming.

With this guideline in mind, lets take a look of the implementations, which I created for my previous blog entries. The requirements of the search function are following:

  • It must be possible to search persons by using their last name as a search criteria.
  • The search function must return only such persons whose last name begins with the given search term.
  • The search must be case insensitive.

First, lets take a look of the implementation which is using the JPA Criteria API. The source code of my static meta model is given in following:

@StaticMetamodel(Person.class)
public class Person_ {
    public static volatile SingularAttribute<Person, String> lastName;
}

The source code of my specification builder class is given in following:

public class PersonSpecifications {

/**
     * Creates a specification used to find persons whose last name begins with
     * the given search term. This search is case insensitive.
     * @param searchTerm
     * @return
     */
    public static Specification<Person> lastNameIsLike(final String searchTerm) {
        
        return new Specification<Person>() {
            @Override
            public Predicate toPredicate(Root<Person> personRoot, CriteriaQuery<?> query, CriteriaBuilder cb) {
                String likePattern = getLikePattern(searchTerm);                
                return cb.like(cb.lower(personRoot.<String>get(Person_.lastName)), likePattern);
            }
            
            private String getLikePattern(final String searchTerm) {
                StringBuilder pattern = new StringBuilder();
                pattern.append(searchTerm.toLowerCase());
                pattern.append("%");
                return pattern.toString();
            }
        };
    }
}

Second, the source code of the implementations which uses Querydsl is given in following:

public class PersonPredicates {

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

This use case is pretty simple but it can still be used for demonstrating the differences of the JPA Criteria API and the Querydsl. The source code written by using Querydsl is clearly more readable than the one using the JPA Criteria API. Also, when the queries become more complex, the difference will be much bigger.

I would say that this round goes to Querydsl.

Testability

Software testability is the degree to which a software artifact (i.e. a software system, software module, requirements or design document) supports testing in a given context.

– Wikipedia.

In other words, the testability of your code defines the amount and quality of tests you can write at the same cost. If the testability of your code is high, you can write more tests with better quality than in a situation where the testability of your code is low.

Lets keep this measurement in mind when we will compare the unit tests written for implementations which were presented earlier.

First, lets check out the unit test for the implementation which uses the JPA Criteria API:

public class PersonSpecificationsTest {
    
    private static final String SEARCH_TERM = "Foo";
    private static final String SEARCH_TERM_LIKE_PATTERN = "foo%";
    
    private CriteriaBuilder criteriaBuilderMock;
    
    private CriteriaQuery criteriaQueryMock;
    
    private Root<Person> personRootMock;

@Before
    public void setUp() {
        criteriaBuilderMock = mock(CriteriaBuilder.class);
        criteriaQueryMock = mock(CriteriaQuery.class);
        personRootMock = mock(Root.class);
    }

@Test
    public void lastNameIsLike() {
        Path lastNamePathMock = mock(Path.class);        
        when(personRootMock.get(Person_.lastName)).thenReturn(lastNamePathMock);
        
        Expression lastNameToLowerExpressionMock = mock(Expression.class);
        when(criteriaBuilderMock.lower(lastNamePathMock)).thenReturn(lastNameToLowerExpressionMock);
        
        Predicate lastNameIsLikePredicateMock = mock(Predicate.class);
        when(criteriaBuilderMock.like(lastNameToLowerExpressionMock, SEARCH_TERM_LIKE_PATTERN)).thenReturn(lastNameIsLikePredicateMock);

Specification<Person> actual = PersonSpecifications.lastNameIsLike(SEARCH_TERM);
        Predicate actualPredicate = actual.toPredicate(personRootMock, criteriaQueryMock, criteriaBuilderMock);
        
        verify(personRootMock, times(1)).get(Person_.lastName);
        verifyNoMoreInteractions(personRootMock);
        
        verify(criteriaBuilderMock, times(1)).lower(lastNamePathMock);
        verify(criteriaBuilderMock, times(1)).like(lastNameToLowerExpressionMock, SEARCH_TERM_LIKE_PATTERN);
        verifyNoMoreInteractions(criteriaBuilderMock);

verifyZeroInteractions(criteriaQueryMock, lastNamePathMock, lastNameIsLikePredicateMock);

assertEquals(lastNameIsLikePredicateMock, actualPredicate);
    }
}

Second, the unit test for the implementation using Querydsl is given in following:

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

After seeing the unit tests for both implementations, it should be obvious that writing unit tests for Querydsl is much easier than writing unit tests for the JPA Criteria API. Also, the unit test written to test the Querydsl predicate builder is much easier to understand. This is valuable because unit tests should also be used to document the behavior of the system.

At this point it should be clear that the winner of this round is Querydsl

PS. I am aware that unit tests do no ensure that the results returned by the created query are correct. However, I believe that they are still valuable because running unit tests is typically dramatically faster than running integration tests. It is still good to understand that in the context of integration testing, the testability of both implementations is equal.

Conclusions

The question is:

Should I use the JPA Criteria API or Querydsl?

It depends. If you are starting from scratch and you have a total control over your technology selections, you should at least consider using Querydsl. It makes your code easier to write and read. It also means that writing unit tests for your code is simpler and faster.

On the other hand, if you are modifying an existing system to use Spring Data JPA, and the existing code is using the JPA Criteria API, you might want to continue using it for the sake of consistency.

The fact is that there is no right answer for this question. The answer depends always from external requirements. The only thing you can do, is to ensure that you are aware of the different options, which are available to you. Only then you can choose the right tool for the task in hand.

There is Still More to Learn

The truth is that I have only scratched the surface of implementing JPA based repositories. I hope that the recommendations given in this blog entry will help you to take the first step, but I have to admit that there is a lot more to learn. I hope that the following resources will help you in your journey:

Reference Documentation

JPA Criteria API 2.0

Querydsl

Spring Data JPA Tutorial Part Nine: Conclusions(未翻译)的更多相关文章

  1. Spring Data JPA教程, 第二部分: CRUD(翻译)

    我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用.该应用要求如下: pe ...

  2. Spring Data JPA教程, 第八部分:Adding Functionality to a Repository (未翻译)

    The previous part of my tutorial described how you can paginate query results with Spring Data JPA. ...

  3. Spring Data JPA教程, 第七部分: Pagination(未翻译)

    The previous part of my Spring Data JPA tutorialdescribed how you can sort query results with Spring ...

  4. Spring Data JPA教程, 第六部分: Sorting(未翻译)

    The fifth part of my Spring Data JPA tutorialdescribed how you can create advanced queries with Spri ...

  5. Spring Data JPA教程, 第四部分: JPA Criteria Queries(未翻译)

    The third part of my Spring Data JPA tutorialdescribed how you can create custom queries by using qu ...

  6. Spring Data JPA教程,第一部分: Configuration(翻译)

    Spring Data JPA项目旨在简化基于仓库的JPA的创建并减少与数据库交互的所需的代码量.本人在自己的工作和个人爱好项目中已经使用一段时间,它却是是事情如此简单和清洗,现在是时候与你分享我的知 ...

  7. Spring Data JPA教程, 第五部分: Querydsl(未翻译)

    The fourth part of my Spring Data JPA tutorialdescribed how you can implement more advanced queries ...

  8. Spring Data JPA 教程(翻译)

    写那些数据挖掘之类的博文 写的比较累了,现在翻译一下关于spring data jpa的文章,觉得轻松多了. 翻译正文: 你有木有注意到,使用Java持久化的API的数据访问代码包含了很多不必要的模式 ...

  9. 快速搭建springmvc+spring data jpa工程

    一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...

随机推荐

  1. Difference between 2>&-, 2>/dev/null, |&, &>/dev/null and >/dev/null 2>&1

    Reference link: http://unix.stackexchange.com/questions/70963/difference-between-2-2-dev-null-dev-nu ...

  2. poj 3264 Balanced Lineup (RMQ算法 模板题)

    RMQ支持操作: Query(L, R):  计算Min{a[L],a[L+1], a[R]}. 预处理时间是O(nlogn), 查询只需 O(1). RMQ问题 用于求给定区间内的最大值/最小值问题 ...

  3. ZJOI2006物流运输

    唉,没想出来…… 注意到预处理的作用.还有CLJ大牛说的话:这么小的数据,想干什么都可以. SPFA预处理+DP 够经典 var f:..,..]of longint; a:..,..]of bool ...

  4. 在fmri研究中,cca的应用历史

    1.02年ola是第一个应用cca在fmri激活检测上的学者. <exploratory fmri analysis by autocorrelation maximization> 2. ...

  5. shell中for循环总结

    关于shell中的for循环用法很多,一直想总结一下,今天网上看到上一篇关于for循环用法的总结,感觉很全面,所以就转过来研究研究,嘿嘿... 1. for((i=1;i<=10;i++));d ...

  6. delphi 注册 dcc70.dll

    @echo 开始注册copy dcc70.dll %windir%\system32\regsvr32 %windir%\system32\dcc70.dll /s@echo dcc70.dll注册成 ...

  7. MyBatis学习 之 二、SQL语句映射文件(2)增删改查、参数、缓存

    目录(?)[-] 二SQL语句映射文件2增删改查参数缓存 select insert updatedelete sql parameters 基本类型参数 Java实体类型参数 Map参数 多参数的实 ...

  8. 【转载】图论 500题——主要为hdu/poj/zoj

    转自——http://blog.csdn.net/qwe20060514/article/details/8112550 =============================以下是最小生成树+并 ...

  9. [Papers]NSE, $\p_3u$, Lebesgue space [Kukavica-Ziane, JMP, 2007]

    $$\bex \p_3\bbu\in L^p(0,T;L^q(\bbR^3)),\quad \frac{2}{p}+\frac{3}{q}=2,\quad \frac{9}{4}\leq q\leq ...

  10. NEUOJ711 异星工厂 字典树+贪心

    题意:你可以收集两个不相交区间的权值,区间权值是区间异或,问这两个权值和最大是多少 分析:很多有关异或求最大的题都是利用01字典树进行贪心,做这个题的时候我都忘了...最后是看别人代码的时候才想起来这 ...