0Pricing
Spring Boot 4 Complete Guide · Pelajaran

Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria

Susun predikat dinamis yang aman secara tipe dengan API Spesifikasi JPA untuk penyaringan berbasis runtime.

Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria adalah pelajaran Spring Boot 4 Complete Guide gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Spring Boot 4 Complete Guide, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Bagian dari pelajaran ini belum diterjemahkan dan ditampilkan dalam bahasa Inggris.

Why Dynamic Filtering?

Real applications rarely query with fixed criteria. A product search screen might filter by name, category, price range, or any combination the user picks at runtime.

Writing a separate repository method for every combination explodes fast: findByName, findByNameAndCategory, findByCategoryAndPriceBetween... This does not scale.

Spring Data JPA's Specification API lets you compose type-safe query fragments at runtime and combine them with and() / or(). In this lesson you'll build dynamic predicates the clean way.

Enabling Specifications on a Repository

To use Specifications, your repository must extend JpaSpecificationExecutor<T> in addition to JpaRepository.

This adds overloaded methods such as findAll(Specification<T>), findAll(Specification, Pageable), findOne(Specification), and count(Specification).

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

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

Anatomy of a Specification

A Specification<T> is a functional interface with one method:

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)

  • root — the entity you query from; use root.get("field") to reference columns.
  • query — the overall CriteriaQuery (for ordering, distinct, grouping).
  • cb — the CriteriaBuilder factory that builds Predicate objects like equal, like, greaterThan.

Your First Specification

Here is a Specification that filters products by an exact category name. A common idiom is to expose factory methods returning Specifications.

Notice root.get("category") walks into the related Category entity, then .get("name") reaches its field.

import org.springframework.data.jpa.domain.Specification;

public class ProductSpecs {

    public static Specification<Product> hasCategory(String category) {
        return (root, query, cb) ->
                cb.equal(root.get("category").get("name"), category);
    }
}

Combining Specifications

The power of Specifications is composition. Use the static and instance helpers on the interface:

  • Specification.allOf(...) / Specification.anyOf(...) — combine many.
  • spec.and(other) — logical AND.
  • spec.or(other) — logical OR.
  • Specification.not(spec) — negation.

Each combinator returns a new Specification, so chaining stays immutable and readable.

Specification<Product> spec =
        ProductSpecs.hasCategory("Laptops")
            .and(ProductSpecs.priceLessThan(new BigDecimal("2000")));

List<Product> result = productRepository.findAll(spec);

Building Predicates Conditionally

The real win is assembling a Specification only from the filters the user actually supplied. Start from an empty conjunction and append non-null criteria.

In Spring Boot 4, Specification.unrestricted() returns a no-op base that matches everything — the ideal neutral starting point.

public Specification<Product> build(ProductFilter f) {
    Specification<Product> spec = Specification.unrestricted();

    if (f.getName() != null) {
        spec = spec.and(ProductSpecs.nameContains(f.getName()));
    }
    if (f.getCategory() != null) {
        spec = spec.and(ProductSpecs.hasCategory(f.getCategory()));
    }
    if (f.getMaxPrice() != null) {
        spec = spec.and(ProductSpecs.priceLessThan(f.getMaxPrice()));
    }
    return spec;
}

LIKE, Ranges, and Case-Insensitive Matching

The CriteriaBuilder exposes the building blocks you need for flexible text and numeric filters:

  • cb.like(cb.lower(root.get("name")), "%" + term.toLowerCase() + "%") — case-insensitive contains.
  • cb.between(root.get("price"), min, max) — inclusive range.
  • cb.greaterThanOrEqualTo(...) / cb.lessThanOrEqualTo(...) — open-ended bounds.
public static Specification<Product> nameContains(String term) {
    return (root, query, cb) ->
            cb.like(cb.lower(root.get("name")),
                    "%" + term.toLowerCase() + "%");
}

public static Specification<Product> priceLessThan(BigDecimal max) {
    return (root, query, cb) ->
            cb.lessThanOrEqualTo(root.get("price"), max);
}

Null-Safe Specifications

A neat alternative to if guards: let each factory return null when its input is absent. Spring Data treats a null Specification (and a null returned from toPredicate) as "no restriction" and skips it during composition.

This keeps the builder flat, but be explicit — returning null can surprise readers, so document it.

public static Specification<Product> hasCategory(String category) {
    return (root, query, cb) ->
            category == null
                ? null
                : cb.equal(root.get("category").get("name"), category);
}

Joins Inside a Specification

For collection associations or to avoid lazy-loading pitfalls, create an explicit Join from the root. This generates a SQL JOIN and lets you filter on the related entity's columns.

Tip: when a join can multiply rows, call query.distinct(true) to avoid duplicate parents in the result.

import jakarta.persistence.criteria.Join;

public static Specification<Product> hasTag(String tag) {
    return (root, query, cb) -> {
        Join<Product, Tag> tags = root.join("tags");
        query.distinct(true);
        return cb.equal(tags.get("label"), tag);
    };
}

Pagination and Sorting

Specifications combine seamlessly with paging. Pass a Pageable alongside the Specification and Spring Data applies WHERE, ORDER BY, LIMIT, and OFFSET in one query.

The returned Page also runs a count query (using the same Specification) so you get total elements for free.

Pageable pageable = PageRequest.of(0, 20, Sort.by("price").descending());

Page<Product> page = productRepository.findAll(build(filter), pageable);

long total = page.getTotalElements();
List<Product> items = page.getContent();

A Standalone Composition Demo

Specifications are lambdas, so the composition logic itself is plain Java you can reason about without a database. This runnable example mirrors how and()/or() short-circuit by composing boolean predicates the same way.

It demonstrates the mental model: a Specification is a deferred predicate that you combine before executing.

import java.util.List;
import java.util.function.Predicate;

public class SpecDemo {
    record Product(String name, String category, int price) {}

    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("UltraBook", "Laptops", 1800),
            new Product("GamerX", "Laptops", 2400),
            new Product("OfficeMouse", "Accessories", 25)
        );

        Predicate<Product> spec = p -> p.category().equals("Laptops");
        spec = spec.and(p -> p.price() <= 2000);

        products.stream()
                .filter(spec)
                .forEach(p -> System.out.println(p.name()));
    }
}

Quick Check

You are building a dynamic filter where each optional criterion may or may not be present. You want a neutral base Specification that matches all rows, then conditionally add restrictions. Which is the correct Spring Boot 4 starting point?

Recap

You learned how to build runtime-driven, type-safe filters with the JPA Specification API:

  • Extend JpaSpecificationExecutor to unlock findAll(Specification, ...).
  • A Specification is a lambda over (root, query, cb) returning a Predicate.
  • Compose with and(), or(), not(), and start from Specification.unrestricted().
  • Add restrictions conditionally so only supplied filters affect the query.
  • Use cb.like/cb.lower, cb.between, and explicit root.join(...) with query.distinct(true) for richer queries.
  • Pass a Pageable for paging, sorting, and an automatic count query.

This pattern replaces dozens of hand-written finder methods with one clean, composable builder.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria” gratis?

Ya — teks lengkap “Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Spring Boot 4 Complete Guide, upgrade ke CoddyKit PRO. Kursus Spring Boot 4 Complete Guide mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria”?

Susun predikat dinamis yang aman secara tipe dengan API Spesifikasi JPA untuk penyaringan berbasis runtime. Kamu berlatih Spring Boot 4 Complete Guide dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Spring Boot 4 Complete Guide?

Tidak diperlukan pengalaman sebelumnya. Spring Boot 4 Complete Guide di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Spring Boot 4 Complete Guide ini?

Ya. Setiap pelajaran Spring Boot 4 Complete Guide menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Metode Kueri Turunan dan Resolusi Kata Kunci
  2. Kueri JPQL dan Native dengan @Query
  3. Spesifikasi dan Penyaringan Dinamis Berbasis Kriteria
  4. Paginasi, Pengurutan, dan Streaming Potongan
← Kembali ke Spring Boot 4 Complete Guide