Как писать кастомные запросы в Spring Data JPA?

«Как писать кастомные запросы в Spring Data JPA?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Spring Data JPA предлагает несколько способов для определения кастомных запросов.

1. Использование аннотации @Query (JPQL или SQL):

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {

    // JPQL-запрос (работает с сущностями)
    @Query("SELECT b FROM Book b WHERE b.publicationYear > :year")
    List<Book> findBooksPublishedAfter(@Param("year") int year);

    // Нативный SQL-запрос (работает напрямую с таблицами)
    @Query(value = "SELECT * FROM books WHERE title ILIKE %:keyword%", nativeQuery = true)
    List<Book> searchByTitleKeyword(@Param("keyword") String keyword);

    // Модифицирующий запрос (UPDATE/DELETE) требует @Modifying
    @Modifying
    @Query("UPDATE Book b SET b.price = b.price * 1.1 WHERE b.category = :category")
    int increasePriceByCategory(@Param("category") String category);
}

2. Query by Example (QBE):

  • Динамический запрос на основе экземпляра-прототипа сущности.
    Book probe = new Book();
    probe.setAuthor("Tolkien");
    Example<Book> example = Example.of(probe);
    List<Book> result = repository.findAll(example);

3. QueryDSL / Specifications:

  • Specifications: Позволяют строить типобезопасные предикаты для динамических запросов.
  • QueryDSL: Генерирует Q-классы и предоставляет fluent API для сложных запросов. Часто используется вместе с JPAQueryFactory.