Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Criteria API в Hibernate/JPA
Criteria API — это type-safe способ построения динамических SQL запросов в Hibernate. Это альтернатива HQL и нативному SQL с лучшей типизацией и удобством.
Что это и зачем нужна
Проблема с HQL и SQL:
// HQL — строки, нет type-safety
public List<User> findActiveUsers(String role) {
String hql = "FROM User WHERE active = true AND role = ?";
return session.createQuery(hql, User.class)
.setParameter(1, role)
.getResultList();
// Если ошибка в HQL — узнаем только при runtime!
}
// SQL — то же самое
public List<User> findActiveUsers(String role) {
String sql = "SELECT * FROM users WHERE active = true AND role = ?";
return session.createNativeQuery(sql, User.class)
.setParameter(1, role)
.getResultList();
}
Решение — Criteria API:
public List<User> findActiveUsers(String role) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root)
.where(
cb.and(
cb.isTrue(root.get(User_.active)),
cb.equal(root.get(User_.role), role)
)
);
return entityManager.createQuery(query).getResultList();
}
Преимущества:
- Compile-time type checking (используем
User_.active, а не строка) - IDE автодополнение
- Безопаснее рефакторить
- Динамическое построение условий
Основные компоненты
1. CriteriaBuilder — фабрика для построения запроса 2. CriteriaQuery — сам запрос 3. Root — главная таблица (FROM) 4. Predicate — условия (WHERE)
Пример 1: простой SELECT
public List<User> findAllUsers() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root);
return entityManager.createQuery(query).getResultList();
}
Пример 2: WHERE с условиями
public List<User> findUsers(String role, boolean active, int minAge) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (role != null) {
predicates.add(cb.equal(root.get(User_.role), role));
}
predicates.add(cb.equal(root.get(User_.active), active));
predicates.add(cb.greaterThanOrEqualTo(root.get(User_.age), minAge));
query.select(root)
.where(cb.and(predicates.toArray(new Predicate[0])));
return entityManager.createQuery(query).getResultList();
}
Пример 3: JOIN и связи
// Найти всех пользователей с активными заказами
public List<User> findUsersWithActiveOrders() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> userRoot = query.from(User.class);
// JOIN с таблицей заказов
Join<User, Order> orderJoin = userRoot.join(User_.orders, JoinType.INNER);
query.select(userRoot)
.where(cb.isTrue(orderJoin.get(Order_.active)))
.distinct(true);
return entityManager.createQuery(query).getResultList();
}
Пример 4: Aggregation (COUNT, SUM, AVG)
public long countActiveUsers() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<User> root = query.from(User.class);
query.select(cb.count(root))
.where(cb.isTrue(root.get(User_.active)));
return entityManager.createQuery(query).getSingleResult();
}
public double getAverageOrderAmount() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Double> query = cb.createQuery(Double.class);
Root<Order> root = query.from(Order.class);
query.select(cb.avg(root.get(Order_.amount)));
return entityManager.createQuery(query).getSingleResult();
}
Пример 5: GROUP BY и HAVING
public List<OrderStatistics> getOrderStatsByUser() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<OrderStatistics> query = cb.createQuery(OrderStatistics.class);
Root<Order> orderRoot = query.from(Order.class);
query.multiselect(
orderRoot.get(Order_.userId),
cb.count(orderRoot),
cb.sum(orderRoot.get(Order_.amount))
)
.groupBy(orderRoot.get(Order_.userId))
.having(cb.gt(cb.count(orderRoot), 5)); // Более 5 заказов
return entityManager.createQuery(query).getResultList();
}
Пример 6: ORDER BY
public List<User> findUsersOrderedByName(String role) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root)
.where(cb.equal(root.get(User_.role), role))
.orderBy(
cb.asc(root.get(User_.name)),
cb.desc(root.get(User_.createdAt))
);
return entityManager.createQuery(query).getResultList();
}
Важное: Metamodel (User_)
Для type-safe access к атрибутам используется Metamodel:
// Аннотированный класс User с @Entity
@Entity
public class User {
@Id
private Long id;
private String name;
private String role;
private boolean active;
// ...
}
// Автоматически генерируется Metamodel
@StaticMetamodel(User.class)
public class User_ {
public static volatile SingularAttribute<User, Long> id;
public static volatile SingularAttribute<User, String> name;
public static volatile SingularAttribute<User, String> role;
public static volatile SingularAttribute<User, Boolean> active;
// ...
}
Это генерируется процессором аннотаций (Hibernate JPA metamodel generator).
Когда использовать Criteria API
Хорошо использовать:
- Сложные динамические запросы с множеством условий
- Построение запросов в runtime на основе пользовательских фильтров
- Когда нужна type-safety
- Запросы с множеством JOIN'ов и условий
Не рекомендуется:
- Простые запросы (лучше использовать Spring Data методы)
- Очень сложные запросы (нативный SQL будет понятнее)
- Queries с специфичным синтаксисом БД
Практика
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public List<User> search(UserSearchCriteria criteria) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (criteria.getRole() != null) {
predicates.add(cb.equal(root.get(User_.role), criteria.getRole()));
}
if (criteria.getNamePattern() != null) {
predicates.add(cb.like(
cb.lower(root.get(User_.name)),
"%" + criteria.getNamePattern().toLowerCase() + "%"
));
}
if (criteria.getMinAge() != null) {
predicates.add(cb.ge(root.get(User_.age), criteria.getMinAge()));
}
query.select(root)
.where(cb.and(predicates.toArray(new Predicate[0])))
.orderBy(cb.asc(root.get(User_.name)));
return em.createQuery(query)
.setFirstResult(criteria.getOffset())
.setMaxResults(criteria.getLimit())
.getResultList();
}
}
Criteria API мощный инструмент, особенно когда нужна гибкость в построении запросов без потери типизации.