# Criteria API and Specifications — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/q-criteria

> Build dynamic, type-safe queries with the Criteria API and Spring Data Specifications.

## Queries built in code

Search screens with optional filters (name contains, price range, category, in stock) lead to many possible query shapes. Concatenating JPQL strings for each combination is error-prone. The **Criteria API** builds queries programmatically: a `CriteriaBuilder` creates predicates, a `CriteriaQuery` defines selection, `where`, `orderBy` and joins, and the **JPA metamodel** (generated classes such as `Product_.price`) makes attribute references **type-safe**, so renaming a field breaks compilation rather than queries at run time. Criteria code is verbose, so **Spring Data JPA Specifications** wrap it: each filter becomes a small `Specification<T>` that you combine with `and`/`or`, and repositories extending `JpaSpecificationExecutor` run them with paging and sorting. Alternatives include **Querydsl** and **Blaze-Persistence** for more fluent dynamic queries, and jOOQ when you prefer type-safe SQL. Use static JPQL when a query's shape is fixed; use Criteria or Specifications when it is genuinely dynamic.

## Composable Specifications for a product search

Only the filters provided by the user are applied.

```java
public final class ProductSpecs {
    public static Specification<Product> titleContains(String text) {
        return (root, query, cb) -> text == null ? null
            : cb.like(cb.lower(root.get("title")), "%" + text.toLowerCase() + "%");
    }
    public static Specification<Product> priceBetween(BigDecimal min, BigDecimal max) {
        return (root, query, cb) -> {
            if (min == null && max == null) return null;
            if (min == null) return cb.le(root.get("price"), max);
            if (max == null) return cb.ge(root.get("price"), min);
            return cb.between(root.get("price"), min, max);
        };
    }
    public static Specification<Product> inCategory(String category) {
        return (root, query, cb) -> category == null ? null
            : cb.equal(root.join("category").get("slug"), category);
    }
}

public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> { }

Page<Product> page = productRepository.findAll(
    Specification.allOf(ProductSpecs.titleContains(q),
                        ProductSpecs.priceBetween(min, max),
                        ProductSpecs.inCategory(category)),
    PageRequest.of(0, 20, Sort.by("price").ascending()));
```

## LEGO bricks for queries

Each Specification is a LEGO brick representing one filter. A search screen snaps together only the bricks the user selected, instead of keeping a separate pre-built model for every possible combination.

**Quiz:** When are the Criteria API or Spring Data Specifications most useful?

- [ ] For queries whose shape never changes
- [x] For dynamic queries where filters are optional and combined at run time
- [ ] For database migrations
- [ ] For configuring connection pools

*Answer:* For dynamic queries where filters are optional and combined at run time. They build query predicates programmatically, avoiding string concatenation for dynamic filters.
