Как добавить сложную Java-логику в метод репозитория Spring Data JPA?

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

Ответ

Spring Data JPA предоставляет несколько способов для расширения стандартного CRUD-функционала сложной бизнес-логикой.

1. Пользовательские запросы с @Query (JPQL / Нативный SQL): Подходит для сложных, но выразимых в виде одного запроса операций.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE " +
           "(:departmentId IS NULL OR u.department.id = :departmentId) AND " +
           "u.registrationDate BETWEEN :startDate AND :endDate")
    List<User> findComplexUsers(@Param("departmentId") Long departmentId,
                                @Param("startDate") LocalDate start,
                                @Param("endDate") LocalDate end);
}

2. Кастомный репозиторий (Рекомендуемый способ для сложной логики): Позволяет использовать EntityManager, JdbcTemplate, Criteria API и любую Java-логику.

Шаг 1: Создайте интерфейс для кастомных методов.

public interface UserRepositoryCustom {
    List<User> findUsersWithComplexBusinessLogic(SearchFilter filter);
    void performMultiStepUpdate(Long userId, UpdateCommand command);
}

Шаг 2: Реализуйте этот интерфейс. Имя класса реализации должно заканчиваться на Impl (по умолчанию).

public class UserRepositoryImpl implements UserRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<User> findUsersWithComplexBusinessLogic(SearchFilter filter) {
        // 1. Создание CriteriaQuery для динамического построения запроса
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        // ... сложная логика построения предикатов

        // 2. Выполнение нативного SQL, если нужно
        // Query nativeQuery = entityManager.createNativeQuery("...");

        // 3. Любые Java-манипуляции с результатами
        return entityManager.createQuery(query).getResultList();
    }
}

Шаг 3: Расширьте основной репозиторий.

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
    // Стандартные методы Spring Data JPA + кастомные методы
}

Теперь можно использовать:

userRepository.findUsersWithComplexBusinessLogic(filter);
userRepository.save(user); // Стандартный метод

Этот подход сохраняет всю мощь Spring Data JPA, добавляя гибкость для сложных сценариев.