Статьи

JPA — Querydsl Проекции

В моем последнем посте: 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;
 
@Immutable
public 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, что не всегда желательно в приложении. Используйте его, если хотите, но вас предупредили

Несколько ссылок на десерт

  1. Использование типов запросов
  2. Запросы
  3. заказ
  4. Конструкторские проекции

Ссылка: JPA — Querydsl Проекции от нашего партнера JCG Михала Ястака в блоге Warlock’s Мысли .