Ответ
JPA Criteria API — это типобезопасный, объектно-ориентированный способ динамического построения запросов к базе данных в Java. Вместо строк JPQL/SQL запрос конструируется вызовом методов Java.
Ключевые преимущества:
- Типобезопасность: Ошибки (например, опечатки в именах полей) обнаруживаются на этапе компиляции, а не в runtime.
- Защита от SQL-инъекций: Поскольку запрос строится через API, риск инъекций практически исключен.
- Динамические запросы: Идеально подходит для построения запросов с переменным набором условий (например, в сложных фильтрах).
- Рефакторинг: IDE может автоматически переименовывать поля сущностей, используемые в запросах.
Основные компоненты:
CriteriaBuilder— фабрика для создания всех частей запроса (where,select,orderBy).CriteriaQuery<T>— объект, представляющий запрос. Определяет тип результата (T).Root<T>— корневой элемент запроса, представляет сущность, от которой строится запрос.Predicate— условие для предложенияWHERE. Условия можно комбинировать (and,or).
Пример: Поиск сотрудников с зарплатой выше заданной и сортировкой.
EntityManager em = ... // получение EntityManager
CriteriaBuilder cb = em.getCriteriaBuilder();
// 1. Создаем запрос, который вернет список Employee
CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
// 2. Определяем корневую сущность запроса (FROM Employee)
Root<Employee> employee = query.from(Employee.class);
// 3. Строим предикат для условия WHERE (salary > minSalary)
ParameterExpression<Double> minSalaryParam = cb.parameter(Double.class);
Predicate salaryCondition = cb.gt(employee.get("salary"), minSalaryParam);
// 4. Собираем запрос
query.select(employee)
.where(salaryCondition)
.orderBy(cb.desc(employee.get("salary"))); // ORDER BY salary DESC
// 5. Создаем TypedQuery, устанавливаем параметр и выполняем
TypedQuery<Employee> typedQuery = em.createQuery(query);
typedQuery.setParameter(minSalaryParam, 100000.0);
List<Employee> resultList = typedQuery.getResultList();
Сложный пример с динамическими условиями:
List<Predicate> predicates = new ArrayList<>();
if (departmentName != null) {
predicates.add(cb.equal(employee.get("department").get("name"), departmentName));
}
if (minExperience != null) {
predicates.add(cb.ge(employee.get("experienceYears"), minExperience));
}
// Объединяем все условия через AND
query.where(cb.and(predicates.toArray(new Predicate[0])));
Когда использовать: Выбирайте Criteria API для сложных, изменчивых запросов, формируемых в runtime. Для простых статических запросов предпочтительнее использовать именованные запросы (@NamedQuery) или строки JPQL.
Ответ 18+ 🔞
А, ну вот, смотри, этот ваш JPA Criteria API — это такая штука, чтобы запросы к базе строить, но не строками, а как нормальные люди, методами джавы вызывать. Типа чтоб IDE помогала, а не ты сам с опечатками воевал.
Ну и в чём прикол, спросишь?
- Типобезопасность, ёпта! Если название поля в сущности опечатаешь — компилятор сразу тебя за шкирку: «Куда прешь, мудила? Такого поля нет!». А не как с JPQL строками — получишь ошибку только когда уже всё запустишь и накосячишь.
- Про инъекции забудь. Тут нихуя не вставишь лишнего, всё через API.
- Динамика — его конёк. Нужен сложный фильтр, где условия могут быть, а могут и не быть? Вот тут он охуенно рулит. Строками JPQL эту хуйню собирать — просто пиздец, проще на SQL переписать.
- Рефакторинг любишь? Переименовал поле в сущности — IDE сама везде в коде запросов поправит. Красота, а не жизнь.
Из чего этот зверь состоит, блядь?
CriteriaBuilder— главный по тарелочкам. От него всё пляшет: условия, сортировки, выборки. Фабрика, короче.CriteriaQuery<T>— это сам запрос в сборе. В<T>пишешь, что хочешь получить в ответе.Root<T>— корень, начало начал. От какой сущности запрос пойдёт (типаFROM Employee).Predicate— это кусок условия дляWHERE. Их можно как угодно склеивать (and,or).
Смотри, как это выглядит на практике. Найдём всех, кто получает больше какой-то суммы:
EntityManager em = ... // достали EntityManager, это твои проблемы как
CriteriaBuilder cb = em.getCriteriaBuilder(); // получили строителя
// 1. Говорим: "Хочу запрос, который вернёт мне Employee"
CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
// 2. Говорим: "Берём за основу сущность Employee, блядь"
Root<Employee> employee = query.from(Employee.class);
// 3. Лепим условие: salary > minSalary
ParameterExpression<Double> minSalaryParam = cb.parameter(Double.class);
Predicate salaryCondition = cb.gt(employee.get("salary"), minSalaryParam);
// 4. Собираем всё в кучу: что выбрать, где условие, как отсортировать
query.select(employee)
.where(salaryCondition)
.orderBy(cb.desc(employee.get("salary"))); // от больших зп к меньшим
// 5. Превращаем в рабочий запрос, подставляем значение и поехали
TypedQuery<Employee> typedQuery = em.createQuery(query);
typedQuery.setParameter(minSalaryParam, 100000.0); // вот эта самая минималка
List<Employee> resultList = typedQuery.getResultList(); // получили олигархов
А вот где его реальная сила — динамические условия. Представь фильтр поиска:
List<Predicate> predicates = new ArrayList<>(); // сюда будем скидывать условия
// Если передали название отдела — добавляем условие
if (departmentName != null) {
predicates.add(cb.equal(employee.get("department").get("name"), departmentName));
}
// Если передали минимальный стаж — добавляем ещё
if (minExperience != null) {
predicates.add(cb.ge(employee.get("experienceYears"), minExperience));
}
// А теперь склеиваем всё это добро через AND и суём в WHERE
query.where(cb.and(predicates.toArray(new Predicate[0])));
Вот видишь? Никакого пиздеца со склеиванием строк. Всё чисто, типобезопасно и красиво.
Так когда же его использовать, а? Да когда запрос — хуй пойми что, и условия в нём могут появляться и исчезать как мудаки в очереди. Для простых, раз и навсегда заданных запросов — используй @NamedQuery или обычный JPQL, не еби себе мозг. А вот для сложной динамической хуйни — это твой лучший друг, хоть и выглядит страшновато поначалу.