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

Пройдите тест, узнайте какой профессии подходите
Я предпочитаю
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 предоставляют больше возможностей для контроля. Выбор между ними зависит от конкретной задачи.

Пошаговый план для смены профессии

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

Давайте рассмотрим подходы, которые помогут нам максимально эффективно использовать возможности 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.
Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что такое проекции в Spring JPA?
1 / 5