Перейти к основному содержимому

Руководство по Querydsl с JPA

· 5 мин. чтения

1. Обзор

Querydsl — это обширная платформа Java, которая помогает создавать и выполнять безопасные по типам запросы на предметно-ориентированном языке, похожем на SQL .

В этой статье мы рассмотрим Querydsl с Java Persistence API.

Небольшое замечание: HQL для Hibernate был первым целевым языком для Querydsl, но в настоящее время он поддерживает JPA, JDO, JDBC, Lucene, Hibernate Search, MongoDB, Collections и RDFBean в качестве серверных частей.

2. Подготовка

Давайте сначала добавим необходимые зависимости в наш проект Maven:

<properties>
<querydsl.version>2.5.0</querydsl.version>
</properties>

<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>

А теперь давайте настроим плагин Maven APT:

<project>
<build>
<plugins>
...
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
</project>

JPAAnnotationProcessor найдет типы доменов, аннотированные аннотацией javax.persistence.Entity, и сгенерирует для них типы запросов.

3. Запросы с Querydsl

Запросы строятся на основе сгенерированных типов запросов, которые отражают свойства типов вашего домена. Кроме того, вызовы функций/методов построены полностью типобезопасным способом.

Пути запросов и операции одинаковы во всех реализациях, а также интерфейсы запросов имеют общий базовый интерфейс.

3.1. Сущность и тип запроса Querydsl

Давайте сначала определим простую сущность, которую мы собираемся использовать при рассмотрении примеров:

@Entity
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column
private String firstname;

@Column
private String surname;

Person() {
}

public Person(String firstname, String surname) {
this.firstname = firstname;
this.surname = surname;
}

// standard getters and setters

}

Querydsl создаст тип запроса с простым именем QPerson в том же пакете, что и Person . QPerson можно использовать в качестве статически типизированной переменной в запросах Querydsl в качестве представителя типа Person .

Во- первых , у QPerson есть переменная экземпляра по умолчанию, доступ к которой можно получить как к статическому полю:

QPerson person = QPerson.person;

В качестве альтернативы вы можете определить свои собственные переменные Person следующим образом:

QPerson person = new QPerson("Erich", "Gamma");

3.2. Создание запроса с использованием JPAQuery

Теперь мы можем использовать экземпляры JPAQuery для наших запросов:

JPAQuery query = new JPAQuery(entityManager);

Обратите внимание, что entityManager — это EntityManager JPA .

Давайте теперь в качестве быстрого примера извлечем всех людей с именем « Кент »:

QPerson person = QPerson.person;
List<Person> persons = query.from(person).where(person.firstName.eq("Kent")).list(person);

Вызов from определяет источник запроса и проекцию, часть where определяет фильтр, а список сообщает Querydsl о необходимости возврата всех совпадающих элементов.

Мы также можем использовать несколько фильтров:

query.from(person).where(person.firstName.eq("Kent"), person.surname.eq("Beck"));

Или же:

query.from(person).where(person.firstName.eq("Kent").and(person.surname.eq("Beck")));

В родной форме JPQL запрос будет записан так:

select person from Person as person where person.firstName = "Kent" and person.surname = "Beck"

Если вы хотите объединить фильтры с помощью «или», используйте следующий шаблон:

query.from(person).where(person.firstName.eq("Kent").or(person.surname.eq("Beck")));

4. Упорядочивание и агрегация в Querydsl

Давайте теперь посмотрим, как упорядочивание и агрегирование работают в библиотеке Querydsl.

4.1. Заказ

Начнем с упорядочения результатов в порядке убывания по полю фамилии :

QPerson person = QPerson.person;
List<Person> persons = query.from(person)
.where(person.firstname.eq(firstname))
.orderBy(person.surname.desc())
.list(person);

4.2. Агрегация

Давайте теперь воспользуемся простой агрегацией, так как у нас есть несколько доступных (Sum, Avg, Max, Min):

QPerson person = QPerson.person;    
int maxAge = query.from(person).list(person.age.max()).get(0);

4.3. Агрегация с помощью GroupBy

Класс com.mysema.query.group.GroupBy предоставляет функции агрегирования, которые мы можем использовать для агрегирования результатов запроса в памяти.

Вот быстрый пример, когда результат возвращается в виде карты с именем в качестве ключа и максимальным возрастом в качестве значения:

QPerson person = QPerson.person;   
Map<String, Integer> results =
query.from(person).transform(
GroupBy.groupBy(person.firstname).as(GroupBy.max(person.age)));

5. Тестирование с помощью Querydsl

Теперь давайте определим реализацию DAO с помощью Querydsl и определим следующую операцию поиска:

public List<Person> findPersonsByFirstnameQuerydsl(String firstname) {
JPAQuery query = new JPAQuery(em);
QPerson person = QPerson.person;
return query.from(person).where(person.firstname.eq(firstname)).list(person);
}

А теперь давайте создадим несколько тестов с использованием этого нового DAO и воспользуемся Querydsl для поиска вновь созданных объектов Person (реализованных в классе PersonDao ), а в другом тесте будет проверена агрегация с использованием класса GroupBy :

@Autowired
private PersonDao personDao;

@Test
public void givenExistingPersons_whenFindingPersonByFirstName_thenFound() {
personDao.save(new Person("Erich", "Gamma"));
Person person = new Person("Kent", "Beck");
personDao.save(person);
personDao.save(new Person("Ralph", "Johnson"));

Person personFromDb = personDao.findPersonsByFirstnameQuerydsl("Kent").get(0);
Assert.assertEquals(person.getId(), personFromDb.getId());
}

@Test
public void givenExistingPersons_whenFindingMaxAgeByName_thenFound() {
personDao.save(new Person("Kent", "Gamma", 20));
personDao.save(new Person("Ralph", "Johnson", 35));
personDao.save(new Person("Kent", "Zivago", 30));

Map<String, Integer> maxAge = personDao.findMaxAgeByName();
Assert.assertTrue(maxAge.size() == 2);
Assert.assertSame(35, maxAge.get("Ralph"));
Assert.assertSame(30, maxAge.get("Kent"));
}

6. Заключение

В этом руководстве показано, как создать проект JPA с помощью Querydsl.

Полную реализацию этой статьи можно найти в проекте github — это проект maven на основе Eclipse, поэтому его легко импортировать и запускать как есть.

Небольшое примечание: запустите простую сборку maven (mvn clean install), чтобы сгенерировать типы в target/generated-sources , а затем, если вы используете Eclipse, включите папку в качестве исходной папки проекта.