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 ...
随机推荐
- HDU 1255 覆盖的面积 (扫描线 线段树 离散化 矩形面积并)
题目链接 题意:中文题意. 分析:纯手敲,与上一道题目很相似,但是刚开始我以为只是把cnt>=0改成cnt>=2就行了,. 但是后来发现当当前加入的线段的范围之前 还有线段的时候就不行了, ...
- 结构体TABLE_share
struct TABLE_share { static inline TABLE **next_ptr(TABLE *l) { return &l->share_next; } stat ...
- Liunx系统学习一,liunx系统的目录结构及含义
LIUNX系统目录结构: “/” ===>这是linux文件系统的入口,也是整个linux文件系统的根目录,linux不同于windows,没有所谓的C,D,E盘,整个liunx只有一个根分区 ...
- FTP文件上传与下载
实现FTP文件上传与下载可以通过以下两种种方式实现(不知道还有没有其他方式),分别为:1.通过JDK自带的API实现:2.通过Apache提供的API是实现. 第一种方式:使用jdk中的ftpClie ...
- (转载) VS编译duilib项目时候的错误解决方法整理
原文地址:http://blog.csdn.net/x356982611/article/details/30217473 @1:找不到Riched20.lib 用everything等软件搜索下磁盘 ...
- ASP.NET导出EXCEL类
最新ASP.NET导出EXCEL类 说明:可以导出ASP.NET页面和DATAGRID(WebControl)数据,可以导出表单头 using System;using System.Data;usi ...
- Flume OG 与 Flume NG 的区别
1.Flume OG:Flume original generation 即Flume 0.9.x版本 Flume NG:Flume next generation ,即Flume 1.x版本 ...
- C++设计模式——建造者模式
建造者模式 在GOF的<设计模式 可复用面向对象软件的基础>中是这样说的:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 这句话,似懂非懂的.一个复杂对象的创建 ...
- [转] Web前端优化之 Server篇
原文链接: http://lunax.info/archives/3093.html Web 前端优化最佳实践第二部分面向 Server .目前共计有 6 条实践规则.[注,这最多算技术笔记,查看最原 ...
- 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇03:子弹发射》
3.子弹发射 子弹发射概述: 在打飞机游戏中,子弹是自动发射的.子弹与子弹之间间隔一定的时间,玩家通过上下左右控制游戏角色,来达到躲避敌人及击中敌人的操作. 发射原理: 抽象理解为有两个容器存放子弹, ...