Работа с выборкой колонок в Spring JPA: примеры кода

Пройдите тест, узнайте какой профессии подходите и получите бесплатную карьерную консультацию
В конце подарим скидку до 55% на обучение
Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Если вам необходимо выбрать определённые столбцы в Spring JPA, следует воспользоваться проекциями с помощью интерфейсов или применять DTO (Data Transfer Objects). Проекции содержат методы, имена которых отражают названия столбцов, в то время как DTO инкапсулируют данные столбцов в объекты.

Вот пример проекции:

Java
Скопировать код
public interface UserInfo {
  String getUsername(); // Возвращает имя пользователя
  String getEmail(); // Возвращает электронную почту
}

Можно использовать такой интерфейс в репозитории следующим образом:

Java
Скопировать код
List<UserInfo> findUsersByActiveIsTrue();

В качестве примера DTO можно привести:

Java
Скопировать код
public class UserDTO {
  private String username; // Имя пользователя
  private String email; // Электронная почта

  // Конструкторы, геттеры и сеттеры
}

// Пример использования DTO в репозитории с JPQL
@Query("SELECT new com.example.UserDTO(u.username, u.email) FROM User u WHERE u.active = true")
List<UserDTO> findActiveUsers();

Проекции обладают простотой использования, но DTO предоставляют больше возможностей для контроля. Выбор между ними зависит от конкретной задачи.

Пройдите тест и узнайте подходит ли вам сфера IT
Пройти тест

Практическое руководство по выборке отдельных столбцов

Давайте рассмотрим подходы, которые помогут нам максимально эффективно использовать возможности Spring Data JPA.

Кастомные JPQL-запросы для выборки конкретных данных

Вы можете создать специфические JPQL-запросы, используя аннотацию @Query.

Java
Скопировать код
@Query("SELECT u.username, u.email FROM User u")
List<Object[]> fetchUsernamesAndEmails();

Для привязки к конструктору в JPQL-запросе:

Java
Скопировать код
@Query("SELECT new com.example.UserDetails(u.username, u.email, u.status) FROM User u")
List<UserDetails> fetchAllUserDetails();

Здесь UserDetails — это DTO, предназначенное для приема данных из JPQL.

Нативные SQL-запросы для особых случаев

С помощью аннотации @Query возможно использовать нативные SQL-запросы:

Java
Скопировать код
@Query(value = "SELECT username, email FROM users WHERE active = 1", nativeQuery = true)
List<Object[]> fetchActiveUserNamesAndEmails();

Не забывайте, что при работе с нативным SQL обычно требуется ручное сопоставление результатов с DTO.

DTO: выбираем только необходимую информацию

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

Специализированные методы репозитория для упрощения работы

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

Java
Скопировать код
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
  @Query("SELECT u.username FROM User u WHERE u.status = :status")
  List<String> findAllUsernamesByStatus(@Param("status") String status);
}

Это позволит обращаться к ним быстро и не заморачиваться с деталями реализации.

Оценим необходимость в нативных SQL-запросах

Если возможностей JPQL или JPA достаточно для выполнения задания, не стоит усложнять себе работу построением нативных SQL-запросов.

Визуализация

Представьте себе ситуацию: Сущность это как необъятный пейзаж (🏞️), а репозиторий JPA – это ваш фотоаппарат (📷).

И зачастую вам не надо фотографировать все сразу, а только отдельные элементы:

Markdown
Скопировать код
🏞️ Полная Сущность: [Гора, Лес, Река, Небо]
📷 Снимки: [Гора, Река]

Снимок (Проекции/DTO) дает вам только то, что вам реально нужно:

Markdown
Скопировать код
| Полная Сущность 🏞️ | Снимок 📷      |
| ------------------- | -------------- |
| Гора (✔️)           | Гора (✔️)      |
| Лес (❌)             |                |
| Река (✔️)           | Река (✔️)      |
| Небо (❌)            |                |

Каждый DTO – это выборка данных, адаптированная под ваши запросы.

Замечания и особенности

Мощь и эффективность Criteria API

Criteria API предоставляет возможность построения типобезопасных запросов.

Java
Скопировать код
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserDTO> query = cb.createQuery(UserDTO.class);
Root<User> root = query.from(User.class);

query.multiselect(root.get("username"), root.get("email")); // Выбираем имя и почту
TypedQuery<UserDTO> typedQuery = entityManager.createQuery(query);
List<UserDTO> results = typedQuery.getResultList();

Criteria API наиболее эффективно применяется для построения динамических запросов.

Формирование маппинга автоматически

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

Особенности обработки данных при группировке и агрегации

При использовании GROUP BY и агрегирования данных проверьте, соответствуют ли ваши проекции или DTO ожидаемым результатам.

Полезные материалы

  1. Введение в проекции в Spring Data JPA — этот документ описывает работу с проекциями в Spring Data JPA.
  2. Создание эффективных запросов DTO проекции с JPA — статья Влада Михальцы, посвящённая запросам DTO проекции.
  3. Построение запросов с помощью Criteria API — обзор возможностей Criteria API.
  4. Анализ динамических проекций в Spring Data JPA — статья на DZone о динамических проекциях в Spring Data JPA.