Spring Data JPA教程, 第五部分: Querydsl(未翻译)
The fourth part of my Spring Data JPA tutorialdescribed 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 useQuerydsl 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:
<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 yourpom.xml file. The relevant part of the pom.xml looks following:
<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 thetarget/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 actualSpecification 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 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 oftoString() 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 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 tofindAll(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.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 thePersonService 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 thePersonPredicates 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 thesearch(String searchTerm) method is given in following:
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 tutorialare 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 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.
P.S. You might also want to learn how you can use Querydsl in a multi-module Maven project.
Spring Data JPA教程, 第五部分: Querydsl(未翻译)的更多相关文章
- Spring Data JPA教程,第一部分: Configuration(翻译)
		
Spring Data JPA项目旨在简化基于仓库的JPA的创建并减少与数据库交互的所需的代码量.本人在自己的工作和个人爱好项目中已经使用一段时间,它却是是事情如此简单和清洗,现在是时候与你分享我的知 ...
 - 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 ...
 - Spring Data JPA 教程(翻译)
		
写那些数据挖掘之类的博文 写的比较累了,现在翻译一下关于spring data jpa的文章,觉得轻松多了. 翻译正文: 你有木有注意到,使用Java持久化的API的数据访问代码包含了很多不必要的模式 ...
 - Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)
		
在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...
 - 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 ...
 
随机推荐
- Form验证(转)
			
代码写 N 久了,总想写得别的.这不,上头说在整合两个项目,做成单一登录(Single Sign On),也有人称之为“单点登录”.查阅相关文档后,终于实现了,现在把它拿出来与大家一起分享.或许大家会 ...
 - iOS应用的crash日志的分析基础
			
Outline如何获得crash日志如何解析crash日志如何分析crash日志 1. iOS策略相关 2. 常见错误标识 3. 代码bug 一.如何获得crash日志 ...
 - UVA 10735 Euler Circuit 混合图的欧拉回路(最大流,fluery算法)
			
题意:给一个图,图中有部分是向边,部分是无向边,要求判断是否存在欧拉回路,若存在,输出路径. 分析:欧拉回路的定义是,从某个点出发,每条边经过一次之后恰好回到出发点. 无向边同样只能走一次,只是不限制 ...
 - 【C#学习笔记】结构体使用
			
using System; namespace ConsoleApplication { struct _st { public string name; public int age; } clas ...
 - 【转】Linux 之 /etc/profile、~/.bash_profile 等几个文件的执行过程
			
原文网址:http://blog.csdn.net/ithomer/article/details/6322892 在登录Linux时要执行文件的过程如下:在刚登录Linux时,首先启动 /etc/p ...
 - oracle 统计语句 与常见函数的归纳(未完待续)
			
一.统计语句 1. count count(*)与count(0)语句的区别: count(*)统计所有数量 count(0)统计第一列不为空的 2. 两个统计量的减法 select (select ...
 - [转] C# 泛型类型参数的约束
			
啊.紫原文C# 泛型类型参数的约束 在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制.如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误.这些限制 ...
 - 陈发树云南白药股权败诉真相 取胜仅差三步 z
			
22亿元现金,三年只拿到750多万元的利息.福建富豪陈发树的云南生意可谓失望之极.在漫长的官司中,曾经有绝处逢生之机的陈发树,连告状的主体都没有找准,岂能同强大的国企扳手腕?陈发树律师团距取胜只有三步 ...
 - Android selector item 属性大全(按钮按下不同效果)
			
<selector> 必须.必须是根元素.包含一个或多个<item>元素. Attributes: xmlns:and ...
 - Matlab编程实例(2) 同期平均
			
%多点同期平均 close all; clear all; pi = 3.14159; Samp2=input('您需要几组信号做同期平均?') Samp1=1000 %设置采样精度 t = lins ...