Как появляется реализация интерфейса репозитория в Spring Data JPA
Что проверяют
Понимаете ли вы, что за интерфейсом без реализации стоит обычный прокси, собранный при старте контекста, а не магия. Следом спрашивают, где на самом деле живёт код save(), откуда берутся транзакции и как добавить свой метод с ручной реализацией.
Как отвечать
Цепочка по шагам:
- поиск интерфейсов:
@EnableJpaRepositories(в Spring Boot её включает автоконфигурация) сканирует пакеты и находит наследниковRepository:CrudRepository,JpaRepositoryи так далее; - регистрация бинов: для каждого интерфейса регистрируется
JpaRepositoryFactoryBean, который при создании бина обращается кJpaRepositoryFactory; - прокси: фабрика через
ProxyFactoryсобирает JDK dynamic proxy, реализующий ваш интерфейс. Целевой объект за прокси этоSimpleJpaRepository, где и написаныsave,findById,deleteByIdповерхEntityManager; - query methods: имена вроде
findByEmailAndStatusразбираются в деревоPartTree, и из него строится JPA-запрос. Методы с@Queryберут готовый JPQL или нативный SQL; - свои реализации: интерфейс-фрагмент
UserRepositoryCustomи классUserRepositoryCustomImplс постфиксомImpl. Spring находит его и подмешивает в прокси.
Назовите поведение по умолчанию: SimpleJpaRepository помечен @Transactional(readOnly = true), а изменяющие методы переопределяют это обычной транзакцией. Трансляция исключений в DataAccessException на прокси репозитория тоже подключается сама.
Полезная деталь: в режиме инициализации по умолчанию имена методов проверяются при старте. Опечатка в названии свойства роняет запуск с PropertyReferenceException, а не первый запрос в проде.
Где спотыкаются
Говорят, что Spring генерирует SQL. Spring Data строит запрос уровня JPA, а SQL из него делает провайдер, обычно Hibernate.
Уверены, что на интерфейс обязательно вешать @Repository. Для репозиториев Spring Data аннотация не нужна: бин и трансляцию исключений даёт сам механизм.
Называют класс реализации как угодно и удивляются, что Spring пытается вывести запрос из имени метода и падает. Постфикс Impl обязателен, если его не переопределили через repositoryImplementationPostfix.