Specifications og dynamisk filtrering baseret på kriterier
Sammensæt typesikre dynamiske prædikater med JPA Specification API til runtime-baseret filtrering.
Specifications og dynamisk filtrering baseret på kriterier er en gratis Komplet guide til Spring Boot 4-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Komplet guide til Spring Boot 4, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Hvorfor dynamisk filtrering?
Rigtige applikationer forespørger sjældent med faste kriterier. En produktsøgeskærm kan filtrere efter navn, kategori, prisinterval eller enhver kombination, som brugeren vælger under kørsel.
Hvis du skriver en separat repository-metode for hver kombination, vokser det hurtigt voldsomt: findByName, findByNameAndCategory, findByCategoryAndPriceBetween ... Det skalerer ikke.
Spring Data JPA's Specification API lader dig sammensætte typesikre forespørgselsfragmenter under kørsel og kombinere dem med and() / or(). I denne lektion opbygger du dynamiske prædikater på den rene måde.
Aktivering af specifikationer i et repository
Hvis du vil bruge specifikationer, skal dit repository udvide JpaSpecificationExecutor<T> ud over JpaRepository.
Det tilføjer overbelastede metoder som findAll(Specification<T>), findAll(Specification, Pageable), findOne(Specification) og 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> {
}En specifikations opbygning
En Specification<T> er en funktionel grænseflade med én metode:
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb)
- root — den entitet, du forespørger på; brug
root.get("field")til at referere til kolonner. - query — den overordnede
CriteriaQuery(til sortering, entydige resultater og gruppering). - cb — fabrikken
CriteriaBuilder, der opbyggerPredicate-objekter somequal,likeoggreaterThan.
Din første specifikation
Her er en Specification, der filtrerer produkter efter et nøjagtigt kategorinavn. Et almindeligt idiom er at eksponere fabriksmetoder, der returnerer Specifications.
Bemærk, at root.get("category") går ind i den relaterede Category-entitet, hvorefter .get("name") når dens felt.
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);
}
}Kombination af specifikationer
Styrken ved Specifications er sammensætning. Brug de statiske hjælpe- og instansmetoder på grænsefladen:
Specification.allOf(...)/Specification.anyOf(...)— kombinerer mange.spec.and(other)— logisk AND.spec.or(other)— logisk OR.Specification.not(spec)— negation.
Hver kombinationsmetode returnerer en ny Specification, så kædningen forbliver uforanderlig og let at læse.
Specification<Product> spec =
ProductSpecs.hasCategory("Laptops")
.and(ProductSpecs.priceLessThan(new BigDecimal("2000")));
List<Product> result = productRepository.findAll(spec);Betinget opbygning af prædikater
Den egentlige fordel er kun at samle en Specification ud fra de filtre, brugeren faktisk har angivet. Start med en tom konjunktion, og tilføj kriterier, der ikke er null.
I Spring Boot 4 returnerer Specification.unrestricted() et grundlag uden handling, der matcher alt — det ideelle neutrale udgangspunkt.
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, intervaller og uafhængighed af store og små bogstaver
CriteriaBuilder stiller de byggesten til rådighed, som du skal bruge til fleksible tekst- og talfiltre:
cb.like(cb.lower(root.get("name")), "%" + term.toLowerCase() + "%")— indeholder uden hensyn til store og små bogstaver.cb.between(root.get("price"), min, max)— inklusivt interval.cb.greaterThanOrEqualTo(...)/cb.lessThanOrEqualTo(...)— åbne grænser.
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-sikre specifikationer
Et smart alternativ til if-kontroller: lad hver fabrik returnere null, når dens input mangler. Spring Data behandler en null-Specification (og en null, der returneres fra toPredicate) som "ingen begrænsning" og springer den over under sammensætningen.
Det holder builderen enkel, men vær tydelig — det kan overraske læsere, hvis du returnerer null, så dokumentér det.
public static Specification<Product> hasCategory(String category) {
return (root, query, cb) ->
category == null
? null
: cb.equal(root.get("category").get("name"), category);
}Join inde i en specifikation
For samlinger af relaterede entiteter eller for at undgå problemer med lazy loading skal du oprette en eksplicit Join fra roden. Det genererer et SQL-JOIN og lader dig filtrere på den relaterede entitets kolonner.
Tip: Når et join kan multiplicere rækker, skal du kalde query.distinct(true) for at undgå duplikerede overordnede entiteter i resultatet.
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);
};
}Sideopdeling og sortering
Specifications kan kombineres problemfrit med sideopdeling. Giv en Pageable sammen med Specification, så anvender Spring Data WHERE, ORDER BY, LIMIT og OFFSET i én forespørgsel.
Den returnerede Page kører også en count-forespørgsel (med den samme Specification), så du får det samlede antal elementer automatisk.
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();En selvstændig demonstration af sammensætning
Specifications er lambdaudtryk, så selve sammensætningslogikken er almindelig Java, som du kan forstå uden en database. Dette kørbare eksempel afspejler, hvordan and()/or() kortslutter ved at sammensætte booleske prædikater på samme måde.
Det demonstrerer den mentale model: En Specification er et udskudt prædikat, som du kombinerer, før du udfører det.
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()));
}
}Hurtigt tjek
Du opbygger et dynamisk filter, hvor hvert valgfrit kriterium måske er angivet og måske ikke. Du vil have en neutral basisspecifikation, der matcher alle rækker, og derefter tilføje begrænsninger betinget. Hvad er det korrekte udgangspunkt i Spring Boot 4?
Opsummering
Du har lært at opbygge typesikre filtre styret af runtime med JPA Specification API:
- Udvid JpaSpecificationExecutor for at aktivere
findAll(Specification, ...). - En Specification er et lambdaudtryk over
(root, query, cb), der returnerer etPredicate. - Sammensæt med
and(),or()ognot(), og start medSpecification.unrestricted(). - Tilføj begrænsninger betinget, så kun angivne filtre påvirker forespørgslen.
- Brug
cb.like/cb.lower,cb.betweenog eksplicitroot.join(...)sammen medquery.distinct(true)til mere avancerede forespørgsler. - Giv en Pageable for sideopdeling, sortering og en automatisk optællingsforespørgsel.
Dette mønster erstatter dusinvis af håndskrevne findermetoder med én ren builder, der kan sammensættes.
Lær Java med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 21
- Lektioner
- 84
Ofte stillede spørgsmål
Er lektionen “Specifications og dynamisk filtrering baseret på kriterier” gratis?
Ja — alle 3 lektioner i læringssporet Komplet guide til Spring Boot 4, inklusive “Specifications og dynamisk filtrering baseret på kriterier”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Specifications og dynamisk filtrering baseret på kriterier”?
Sammensæt typesikre dynamiske prædikater med JPA Specification API til runtime-baseret filtrering. Du øver dig i Komplet guide til Spring Boot 4 med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Komplet guide til Spring Boot 4?
Der kræves ingen tidligere erfaring. Komplet guide til Spring Boot 4 på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Specifications og dynamisk filtrering baseret på kriterier”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Komplet guide til Spring Boot 4-lektion?
Ja. Alle Komplet guide til Spring Boot 4-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Afledte query-metoder og keyword-opslag
- JPQL og native queries med @Query
- Specifications og dynamisk filtrering baseret på kriterier
- Pagination, sortering og slice-streaming