← Назад к вопросам
Какая дифференцировать с помощью Criteria API?
1.8 Middle🔥 171 комментариев
#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Criteria API в Hibernate/JPA
Criteria API — это типобезопасный способ создания запросов к базе данных программно, без написания SQL. Это альтернатива JPQL (Java Persistence Query Language) и HQL (Hibernate Query Language).
Основные преимущества
Типобезопасность:
// ❌ JPQL — строка, ошибки проявляются в runtime
Query query = em.createQuery(
"SELECT u FROM User u WHERE u.name = :name"
);
// ✅ Criteria API — типобезопасно
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root).where(cb.equal(root.get("name"), "John"));
Динамические запросы: Есть фильтры, которые можно комбинировать, то Criteria удобнее.
Базовая структура
// 1. Получить CriteriaBuilder
CriteriaBuilder cb = em.getCriteriaBuilder();
// 2. Создать CriteriaQuery
CriteriaQuery<User> cq = cb.createQuery(User.class);
// 3. Определить корневой класс
Root<User> root = cq.from(User.class);
// 4. Построить условие
Predicate whereCause = cb.equal(root.get("email"), "user@example.com");
// 5. Собрать запрос
cq.select(root).where(whereCause);
// 6. Создать типизированный запрос
TypedQuery<User> query = em.createQuery(cq);
// 7. Получить результат
List<User> users = query.getResultList();
Примеры типичных операций
Простой SELECT:
// SQL: SELECT * FROM users WHERE age > 18
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root)
.where(cb.greaterThan(root.get("age"), 18));
List<User> adults = em.createQuery(cq).getResultList();
С JOIN:
// SQL: SELECT u FROM users u
// JOIN u.posts p
// WHERE p.likes > 100
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
// Создать JOIN
Join<User, Post> postJoin = root.join("posts");
cq.select(root)
.where(cb.greaterThan(postJoin.get("likes"), 100))
.distinct(true);
List<User> popularUsers = em.createQuery(cq).getResultList();
С GROUP BY и HAVING:
// SQL: SELECT p.category, COUNT(p) as cnt
// FROM posts p
// GROUP BY p.category
// HAVING COUNT(p) > 5
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Post> root = cq.from(Post.class);
cq.multiselect(
root.get("category"),
cb.count(root).alias("cnt")
)
.groupBy(root.get("category"))
.having(cb.greaterThan(cb.count(root), 5L));
List<Object[]> results = em.createQuery(cq).getResultList();
С ORDER BY:
// SQL: SELECT u FROM users u ORDER BY u.created DESC, u.name ASC
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root)
.orderBy(
cb.desc(root.get("created")),
cb.asc(root.get("name"))
);
List<User> users = em.createQuery(cq).getResultList();
Условия и предикаты
Root<User> root = cq.from(User.class);
CriteriaBuilder cb = em.getCriteriaBuilder();
// Сравнение
Predicate equal = cb.equal(root.get("status"), "active");
Predicate notEqual = cb.notEqual(root.get("status"), "deleted");
Predicate greaterThan = cb.greaterThan(root.get("age"), 18);
Predicate lessThan = cb.lessThan(root.get("age"), 65);
Predicate between = cb.between(root.get("age"), 18, 65);
// String операции
Predicate like = cb.like(root.get("email"), "%@gmail.com");
Predicate in = root.get("status").in("active", "pending");
// NULL проверки
Predicate isNull = cb.isNull(root.get("deletedAt"));
Predicate isNotNull = cb.isNotNull(root.get("deletedAt"));
// Комбинирование предикатов (AND, OR)
Predicate combined = cb.and(
cb.equal(root.get("status"), "active"),
cb.greaterThan(root.get("age"), 18),
cb.or(
cb.like(root.get("email"), "%@gmail.com"),
cb.like(root.get("email"), "%@yahoo.com")
)
);
Динамические запросы (практический пример)
public List<User> searchUsers(UserFilter filter) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
List<Predicate> predicates = new ArrayList<>();
// Строим условия динамически
if (filter.getMinAge() != null) {
predicates.add(cb.greaterThanOrEqualTo(
root.get("age"), filter.getMinAge()
));
}
if (filter.getMaxAge() != null) {
predicates.add(cb.lessThanOrEqualTo(
root.get("age"), filter.getMaxAge()
));
}
if (filter.getStatus() != null && !filter.getStatus().isEmpty()) {
predicates.add(root.get("status").in(filter.getStatus()));
}
if (filter.getNamePrefix() != null) {
predicates.add(cb.like(
cb.lower(root.get("name")),
filter.getNamePrefix().toLowerCase() + "%"
));
}
if (filter.getCountry() != null) {
Join<User, Country> countryJoin = root.join("country", JoinType.LEFT);
predicates.add(cb.equal(countryJoin.get("code"), filter.getCountry()));
}
// Комбинируем все условия
if (!predicates.isEmpty()) {
cq.where(cb.and(predicates.toArray(new Predicate[0])));
}
// Добавляем сортировку
if (filter.getSortBy() != null) {
if (filter.isAscending()) {
cq.orderBy(cb.asc(root.get(filter.getSortBy())));
} else {
cq.orderBy(cb.desc(root.get(filter.getSortBy())));
}
}
TypedQuery<User> query = em.createQuery(cq);
// Пагинация
if (filter.getLimit() != null) {
query.setMaxResults(filter.getLimit());
}
if (filter.getOffset() != null) {
query.setFirstResult(filter.getOffset());
}
return query.getResultList();
}
Aggregation функции
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Post> root = cq.from(Post.class);
// COUNT, SUM, AVG, MIN, MAX
cq.multiselect(
cb.count(root), // SELECT COUNT(*)
cb.sum(root.get("likes")), // SELECT SUM(likes)
cb.avg(root.get("views")), // SELECT AVG(views)
cb.min(root.get("created")), // SELECT MIN(created)
cb.max(root.get("updated")) // SELECT MAX(updated)
);
Object[] result = em.createQuery(cq).getSingleResult();
long count = (long) result[0];
Number sum = (Number) result[1];
Subqueries
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
// Subquery: SELECT u FROM users u
// WHERE u.id IN (SELECT p.user_id FROM posts p
// WHERE p.likes > 100)
Subquery<Long> subquery = cq.subquery(Long.class);
Root<Post> postRoot = subquery.from(Post.class);
subquery.select(postRoot.get("user").get("id"))
.where(cb.greaterThan(postRoot.get("likes"), 100));
cq.select(root)
.where(root.get("id").in(subquery));
List<User> users = em.createQuery(cq).getResultList();
Сравнение: Criteria vs JPQL vs Native SQL
| Аспект | Criteria API | JPQL | Native SQL |
|---|---|---|---|
| Типобезопасность | ✅ | ❌ | ❌ |
| Динамичность | ✅ | ❌ | ✅ |
| Производительность | ≈ | ≈ | ✅ |
| Читаемость | ❌ | ✅ | ✅ |
| Сложность | 🔴 | 🟡 | 🟡 |
Когда использовать Criteria API
- ✅ Динамические фильтры (где условия строятся на лету)
- ✅ Когда нужна типобезопасность
- ✅ Complex queries с множеством условий
- ❌ НЕ для простых запросов (используй JPQL)
- ❌ НЕ когда нужна максимальная производительность (Native SQL)
Criteria API — это мощный инструмент для построения сложных, типобезопасных запросов в Java, особенно при работе с динамическими фильтрами.