Що таке Criteria API і коли його використовувати
Criteria API — type-safe спосіб побудови JPQL запитів програмно. Він особливо корисний для динамічних запитів з багатьма опціональними фільтрами, де статичний JPQL був би незруч...
Огляд
Criteria API — type-safe спосіб побудови JPQL запитів програмно. Він особливо корисний для динамічних запитів з багатьма опціональними фільтрами, де статичний JPQL був би незручний.
🟢 Junior Level
Що таке Criteria API
Criteria API — type-safe спосіб будувати JPQL запити програмно.
// JPQL — рядковий запит
"SELECT u FROM User u WHERE u.age > 18"
// Criteria API — type-safe
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(cb.gt(root.get("age"), 18));
Основні компоненти
CriteriaBuilder — фабрика для створення виразів
CriteriaQuery<T> — сам запит
Root<T> — корнева сутність (FROM clause)
Predicate — умова WHERE
🟡 Middle Level
Коли використовувати
✅ Criteria API коли:
- Динамічні запити (багато опціональних фільтрів)
- Type-safe потрібен (компіляція ловить помилки)
- Рефакторинг безпечний
❌ JPQL коли:
- Прості статичні запити
- Легше читати
- Менше коду
Динамічний запит з фільтрами
public List<User> findUsers(String name, Integer minAge, String status) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (minAge != null) {
predicates.add(cb.ge(root.get("age"), minAge));
}
if (status != null) {
predicates.add(cb.equal(root.get("status"), status));
}
query.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(query).getResultList();
}
З Metamodel (type-safe)
// Metamodel генерується автоматично
@StaticMetamodel(User.class)
public abstract class User_ {
public static volatile SingularAttribute<User, String> name;
public static volatile SingularAttribute<User, Integer> age;
}
// Type-safe запит
root.get(User_.age) // замість root.get("age")
🔴 Senior Level
Specification pattern (Spring Data JPA)
public class UserSpecifications {
public static Specification<User> hasName(String name) {
return (root, query, cb) ->
cb.like(root.get(User_.name), "%" + name + "%");
}
public static Specification<User> hasMinAge(int minAge) {
return (root, query, cb) ->
cb.greaterThanOrEqualTo(root.get(User_.age), minAge);
}
}
// Використання
Specification<User> spec = Specification
.where(UserSpecifications.hasName("John"))
.and(UserSpecifications.hasMinAge(18));
List<User> users = userRepository.findAll(spec);
Best Practices
✅ Criteria API для динамічних запитів
✅ Metamodel для type-safety
✅ JPQL для простих статичних запитів
✅ Spring Data JPA Specification
✅ Fetch joins для пов'язаних даних
❌ Criteria API для простих запитів (надмірно)
❌ Без metamodel (string-based prone to errors)
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Criteria API — type-safe спосіб побудови JPQL запитів програмно
- Основні компоненти: CriteriaBuilder, CriteriaQuery, Root, Predicate
- Найкраще для динамічних запитів з багатьма опціональними фільтрами
- Metamodel (User_) забезпечує повну type-safety — помилки на компіляції
- Specification pattern (Spring Data JPA) — переиспользуемі критерії
- Для простих статичних запитів JPQL простіший і читабельніший
Пов’язані теми:
- [[26. Що таке JPQL і чим він відрізняється від SQL]]
- [[28. Як використовувати JOIN FETCH для вирішення проблеми N+1]]
- [[29. Що таке projection в JPA]]
- [[23. Як правильно використовувати @OneToMany і @ManyToOne]]