Что такое Specification в Spring Data JPA?

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

Ответ

Specification — это паттерн из Domain-Driven Design (DDD), реализованный в Spring Data JPA (через модуль JpaSpecificationExecutor). Он позволяет инкапсулировать критерии выборки данных в объекты, что делает построение динамических, сложных запросов типобезопасным и удобным для повторного использования.

Для чего это нужно? Вместо создания множества методов репозитория (findByAAndB, findByCOrD) или использования @Query с условным сбором строки запроса, вы создаете отдельные объекты Specification<T>, которые затем комбинируете.

Основные компоненты:

  1. Интерфейс JpaSpecificationExecutor<T>, который добавляется к вашему репозиторию. Он предоставляет методы типа findAll(Specification<T> spec).
  2. Класс Specifications (в более старых версиях) или статические методы класса Specification (в новых) для построения и комбинирования спецификаций.

Пример: Допустим, есть сущность Customer.

// 1. Репозиторий с поддержкой Specification
public interface CustomerRepository extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}

// 2. Создание спецификаций (обычно в отдельном классе)
public class CustomerSpecs {
    public static Specification<Customer> hasName(String name) {
        return (root, query, cb) -> name == null ? null : cb.equal(root.get("name"), name);
    }
    public static Specification<Customer> isActive() {
        return (root, query, cb) -> cb.isTrue(root.get("active"));
    }
}

// 3. Использование в сервисе
@Service
public class CustomerService {
    @Autowired
    private CustomerRepository repository;

    public List<Customer> findActiveCustomersByName(String name) {
        Specification<Customer> spec = Specification.where(CustomerSpecs.isActive())
                                                    .and(CustomerSpecs.hasName(name));
        return repository.findAll(spec);
    }
}

Почему это полезно: Спецификации делают код запросов более модульным, тестируемым и избегают проблемы "раздутого" репозитория. Они особенно эффективны для динамических фильтров в API поиска.