Репозитории Spring Data: JpaRepository и магия методов
В Spring Data вы не пишете реализацию репозитория — только интерфейс. Spring сам генерирует CRUD и даже запросы по имени метода.
Суть: унаследуйте интерфейс от JpaRepository — и получите save, findById, findAll, delete бесплатно. Метод findByEmail(String) Spring сам превратит в SELECT ... WHERE email = ?.
Слой доступа к данным традиционно состоял из тонн однотипного кода: открыть соединение, подготовить запрос, пробежать по результату. Spring Data убирает этот шаблон почти полностью: вы описываете, ЧТО хотите получить, а не КАК это сделать.
Репозиторий = интерфейс
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
// CRUD уже есть: save, findById, findAll, deleteById ...
// Производные запросы — Spring сгенерирует SQL по имени
Optional<User> findByEmail(String email);
List<User> findByAgeGreaterThan(int age);
List<User> findByNameContainingIgnoreCase(String part);
boolean existsByEmail(String email);
}
Параметры JpaRepository<User, Long> означают: сущность User, тип ключа Long. Реализацию этого интерфейса Spring создаст сам в рантайме — вам её писать не нужно.
Производные запросы
Имя метода — это мини-язык запросов. Spring разбирает его на части: findBy + поле + условие. findByAgeGreaterThan читается как «найти, где age больше». Ключевые слова: And, Or, Between, Like, Containing, OrderBy, IgnoreCase.
Как работает под капотом
При старте Spring Data находит интерфейсы-репозитории и для каждого создаёт прокси — динамический объект-реализацию. Для каждого метода прокси либо берёт готовую реализацию CRUD, либо разбирает имя метода и строит JPQL/SQL. К моменту работы приложения у вас есть полноценный бин-репозиторий.
interface UserRepository extends JpaRepository
| Spring Data при старте
v
создаёт ПРОКСИ-реализацию
|
+-- save/findById/... -> готовый CRUD
+-- findByEmail -> разбор имени -> SELECT ... WHERE email = ?
+-- findByAgeGreaterThan -> SELECT ... WHERE age > ?
v
бин UserRepository готов к внедрению
Смоделируем разбор имени метода в запрос-фильтр:
# "Производный запрос": имя метода -> фильтр над коллекцией
users = [
{"name": "Анна", "email": "[email protected]", "age": 30},
{"name": "Борис", "email": "[email protected]", "age": 25},
{"name": "Анна", "email": "[email protected]", "age": 40},
]
def find_by_email(email):
return [u for u in users if u["email"] == email]
def find_by_age_greater_than(age):
return [u for u in users if u["age"] > age]
def find_by_name_containing(part):
return [u for u in users if part.lower() in u["name"].lower()]
print(find_by_email("[email protected]"))
print(find_by_age_greater_than(28))
print(find_by_name_containing("ан"))
Нажмите «Попробуй сам ▶»: каждое «имя метода» превращается в фильтр. Spring делает то же, но переводит фильтр в SQL и выполняет в базе.
Частые ошибки
- Опечатка в имени поля.
findByEmial— Spring не найдёт поле и упадёт на старте (это лучше, чем в рантайме). - Слишком длинные имена.
findByAAndBAndCOrderByDнечитаемо — для сложного лучше@Query. - findBy для одного, возвращающий List. Если ожидается один результат, возвращайте
Optional<User>, а не список.
Best practices
- Используйте производные запросы для простых условий — это лаконично и читаемо.
- Для поиска одной записи возвращайте
Optional, для проверки —existsBy.../countBy.... - Когда имя метода становится громоздким, переходите на
@Query(следующий урок).
Итог: репозиторий в Spring Data — это интерфейс без реализации. CRUD достаётся из коробки, а производные запросы генерируются по имени метода. Меньше кода — меньше ошибок.
Закрепим главное
Главная идея Spring Data — декларативность: вы описываете намерение, а не реализацию. Интерфейс-репозиторий без единой строки тела превращается в полноценный компонент с CRUD и осмысленными запросами. Это резко сокращает объём кода доступа к данным и убирает целый класс ошибок, которые раньше возникали при ручной работе с JDBC.
Полезно понимать границу применимости производных запросов. Пока условие простое — одно-два поля, понятное сравнение — имя метода читается отлично и не требует комментариев. Но как только в имени появляется три и более условий со связками And/Or, оно становится нечитаемым ребусом. Это сигнал перейти на @Query с явным JPQL. Выбирайте инструмент по сложности запроса: простое — именем метода, сложное — явным запросом. И помните про возвращаемые типы: для одной записи — Optional, для проверки наличия — existsBy, для подсчёта — countBy; это делает намерение метода однозначным.