В моем последнем посте: JPA — Основные проекции — я упомянул о двух основных возможностях построения JPA-проекций. В этом посте приводятся дополнительные примеры, на этот раз основанные на платформе Querydsl . Обратите внимание, что я имею в виду Querydsl версии 3.1.1 здесь.
Изобретенные выражения конструктора
Посмотрите на следующий код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
...import static com.blogspot.vardlokkur.domain.QEmployee.employee;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import org.springframework.beans.factory.annotation.Autowired;import com.blogspot.vardlokkur.domain.EmployeeNameProjection;import com.mysema.query.jpa.JPQLTemplates;import com.mysema.query.jpa.impl.JPAQuery;import com.mysema.query.types.ConstructorExpression;...public class ConstructorExpressionExample { ... @PersistenceContext private EntityManager entityManager; @Autowired private JPQLTemplates jpqlTemplates; public void someMethod() { ... final List<EmployeeNameProjection> projections = new JPAQuery(entityManager, jpqlTemplates) .from(employee) .orderBy(employee.name.asc()) .list(ConstructorExpression.create(EmployeeNameProjection.class, employee.employeeId, employee.name)); ... } ...} |
Вышеприведенная конструкция Querydsl означает: создать новый запрос JPQL [1] [2] , используя employee в качестве источника данных, упорядочить данные, используя имя employee [3] , и вернуть список EmployeeNameProjection , построенный с использованием конструктора 2-arg, вызываемого с помощью идентификатор и имя сотрудника [4] . Это очень похоже на пример выражений конструктора из моего предыдущего поста ( JPA — Основные проекции ) и приводит к следующему SQL-запросу:
|
1
|
>select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc |
Как вы видите выше, основным преимуществом по сравнению с выражениями конструктора JPA является использование Java-класса вместо его имени, жестко запрограммированного в JPQL-запросе.
Еще больше изобретенных выражений конструктора
Документация Querydsl [4] описывает другой способ использования выражений конструктора, требующий аннотации @QueryProjection и использования Query Type [1] для проекции, см. Пример ниже. Давайте начнем с модификации класса проекции — обратите внимание, что я добавил аннотацию @QueryProjection в конструктор класса.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.blogspot.vardlokkur.domain;import java.io.Serializable;import javax.annotation.concurrent.Immutable;import com.mysema.query.annotations.QueryProjection;@Immutablepublic class EmployeeNameProjection implements Serializable { private final Long employeeId; private final String name; @QueryProjection public EmployeeNameProjection(Long employeeId, String name) { super(); this.employeeId = employeeId; this.name = name; } public Long getEmployeeId() { return employeeId; } public String getName() { return name; }} |
Теперь мы можем использовать модифицированный класс проекции (и соответствующий тип запроса [1] ) следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
...import static com.blogspot.vardlokkur.domain.QEmployee.employee; import javax.persistence.EntityManager;import javax.persistence.PersistenceContext; import org.springframework.beans.factory.annotation.Autowired; import com.blogspot.vardlokkur.domain.EmployeeNameProjection;import com.blogspot.vardlokkur.domain.QEmployeeNameProjection;import com.mysema.query.jpa.JPQLTemplates;import com.mysema.query.jpa.impl.JPAQuery;... public class ConstructorExpressionExample { ... @PersistenceContext private EntityManager entityManager; @Autowired private JPQLTemplates jpqlTemplates; public void someMethod() { ... final List<EmployeeNameProjection> projections = new JPAQuery(entityManager, jpqlTemplates) .from(employee) .orderBy(employee.name.asc()) .list(new QEmployeeNameProjection(employee.employeeId, employee.name)); ... } ...} |
Что приводит к запросу SQL:
|
1
|
select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc |
Фактически, если вы внимательно посмотрите на тип запроса [1], сгенерированный для EmployeeNameProjection ( QEmployeeNameProjection ), вы увидите, что это своего рода «ярлык» для создания выражения конструктора, как описано в первом разделе этого поста.
Картографическая проекция
Querydsl предоставляет другой способ построения проекций, используя фабрики, основанные на MappingProjection .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package com.blogspot.vardlokkur.domain;import static com.blogspot.vardlokkur.domain.QEmployee.employee;import com.mysema.query.Tuple;import com.mysema.query.types.MappingProjection;public class EmployeeNameProjectionFactory extends MappingProjection<EmployeeNameProjection> { public EmployeeNameProjectionFactory() { super(EmployeeNameProjection.class, employee.employeeId, employee.name); } @Override protected EmployeeNameProjection map(Tuple row) { return new EmployeeNameProjection(row.get(employee.employeeId), row.get(employee.name)); }} |
Приведенный выше класс представляет собой простую фабрику, создающую экземпляры EmployeeNameProjection с использованием идентификатора и имени сотрудника. Обратите внимание, что конструктор фабрики определяет, какие свойства сотрудника будут использоваться для построения проекции, а метод map определяет, как будут создаваться экземпляры.
Ниже вы можете найти пример использования фабрики:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
...import static com.blogspot.vardlokkur.domain.QEmployee.employee; import javax.persistence.EntityManager;import javax.persistence.PersistenceContext; import org.springframework.beans.factory.annotation.Autowired; import com.blogspot.vardlokkur.domain.EmployeeNameProjection;import com.blogspot.vardlokkur.domain.EmployeeNameProjectionFactory import com.mysema.query.jpa.JPQLTemplates;import com.mysema.query.jpa.impl.JPAQuery;... public class MappingProjectionExample { ... @PersistenceContext private EntityManager entityManager; @Autowired private JPQLTemplates jpqlTemplates; public void someMethod() { ... final List<EmployeeNameProjection> projections = new JPAQuery(entityManager, jpqlTemplates) .from(employee) .orderBy(employee.name.asc()) .list(new EmployeeNameProjectionFactory()); .... } ...} |
Как видите, единственное отличие здесь, по сравнению с примерами выражений конструктора, заключается в вызове метода list .
Приведенный выше пример снова приводит к очень простому запросу SQL:
|
1
|
select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc |
Построение проекций таким способом намного мощнее и не требует наличия конструктора проекций n-arg.
Проекция на основе QBean (JavaBeans снова наносит удар)
Существует по крайней мере еще одна возможность создания проекции на основе Querydsl — на основе QBean — в этом случае мы строим список результатов, используя:
|
1
|
... .list(Projections.bean(EmployeeNameProjection.class, employee.employeeId, employee.name)) |
Этот способ требует, чтобы класс EmployeeNameProjection соответствовал соглашениям JavaBean, что не всегда желательно в приложении. Используйте его, если хотите, но вас предупредили