← Назад к вопросам

Какая дифференцировать с помощью 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 APIJPQLNative SQL
Типобезопасность
Динамичность
Производительность
Читаемость
Сложность🔴🟡🟡

Когда использовать Criteria API

  • ✅ Динамические фильтры (где условия строятся на лету)
  • ✅ Когда нужна типобезопасность
  • ✅ Complex queries с множеством условий
  • ❌ НЕ для простых запросов (используй JPQL)
  • ❌ НЕ когда нужна максимальная производительность (Native SQL)

Criteria API — это мощный инструмент для построения сложных, типобезопасных запросов в Java, особенно при работе с динамическими фильтрами.

Какая дифференцировать с помощью Criteria API? | PrepBro