Spring Data JPA Tutorial Part Nine: Conclusions(未翻译)
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:
- Part One: Configuration
 - Part Two: CRUD
 - Part Three: Custom Queries with Query Methods
 - Part Four: JPA Criteria Queries
 - Part Five: Querydsl
 - Part Six: Sorting
 - Part Seven: Pagination
 - Part Eight: Adding Functionality to a Repository
 - Part Nine: Conclusions
 
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:
public class Person_ {
public static volatile SingularAttribute<Person, String> lastName;
}
The source code of my specification builder class is given in following:
/**
     * 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 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:
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:
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
- Dynamic, Typesafe Query in JPA 2.0
 - JPA Criteria API by Samples Part I and Part II
 - Using the Criteria API to Create Queries – The Java EE 6 Tutorial
 
Querydsl
Spring Data JPA Tutorial Part Nine: Conclusions(未翻译)的更多相关文章
- Spring Data JPA教程, 第二部分: CRUD(翻译)
		
我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用.该应用要求如下: pe ...
 - 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. ...
 - Spring Data JPA教程, 第七部分: Pagination(未翻译)
		
The previous part of my Spring Data JPA tutorialdescribed how you can sort query results with Spring ...
 - Spring Data JPA教程, 第六部分: Sorting(未翻译)
		
The fifth part of my Spring Data JPA tutorialdescribed how you can create advanced queries with Spri ...
 - Spring Data JPA教程, 第四部分: JPA Criteria Queries(未翻译)
		
The third part of my Spring Data JPA tutorialdescribed how you can create custom queries by using qu ...
 - Spring Data JPA教程,第一部分: Configuration(翻译)
		
Spring Data JPA项目旨在简化基于仓库的JPA的创建并减少与数据库交互的所需的代码量.本人在自己的工作和个人爱好项目中已经使用一段时间,它却是是事情如此简单和清洗,现在是时候与你分享我的知 ...
 - Spring Data JPA教程, 第五部分: Querydsl(未翻译)
		
The fourth part of my Spring Data JPA tutorialdescribed how you can implement more advanced queries ...
 - Spring Data JPA 教程(翻译)
		
写那些数据挖掘之类的博文 写的比较累了,现在翻译一下关于spring data jpa的文章,觉得轻松多了. 翻译正文: 你有木有注意到,使用Java持久化的API的数据访问代码包含了很多不必要的模式 ...
 - 快速搭建springmvc+spring data jpa工程
		
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
 
随机推荐
- PL/SQL database character set(AL32UTF8) and Client character set(ZHS16GBK) are different 2012-04-11 13:01
			
启动PL/SQL Developer 报字符编码不一致错误 Database character set (AL32UTF8) and Client character set (ZHS16GBK) ...
 - iOS开发:本地数据存储-NSUserDefaults
			
Getting Default Values arrayForKey(_:) boolForKey(_:) dataForKey(_:) dictionaryForKey(_:) floatForKe ...
 - ASP.NET MVC 学习3、Controller左手从Model获取数据,右手传递到View页面
			
参考:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/accessing-your-models-dat ...
 - asp.net的decimal保留两位小数
			
C#的decimal保留两位小数 方法一: decimal d = 46.28111; string dStr = Math.Round( d,2 ).ToString(); 结果:dStr = 46 ...
 - Delegate 委托复习(-) 委托的基本概念
			
1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型. 声明一个代理的例子: public delegate int MyDelegate(stri ...
 - BZOJ 3573 米特运输
			
语文题... 原来除了hash还可以取对数啊orz #include<iostream> #include<cstdio> #include<cstring> #i ...
 - Android adb install INSTALL_FAILED_DEXOPT
			
说明: 之前一直认为将eclipse的Android项目直接cp到Android源码下编译就行了,实际情况是还要注意其他的文件架构. 错误现象: c:\Users\zengjf>adb inst ...
 - RPi 2B Documentation
			
/********************************************************************** * RPi 2B Documentation * 声明: ...
 - Ruby窗口程序
			
require 'tk' tkroot=TkRoot.new { title 'hellw word' geometry '300x200' } lb = TkLabel.new(tkroot) do ...
 - JavaEE参考示例 SpringSide 4.0 GA版杀青
			
SpringSide是以Spring Framework为核心的,Pragmatic风格的JavaEE应用参考示例,是JavaEE世界中的主流技术选型,较佳实践的总结与演示. 经过漫长的7个月和6个R ...