Skip to content

Subhransu-De/QueryDSL

Repository files navigation

QueryDSL Example — Spring Boot + JPA

A focused example project demonstrating core QueryDSL capabilities with Spring Boot, Spring Data JPA, and PostgreSQL.

QueryDSL patterns covered

1. Dynamic predicates with BooleanBuilder

Build a query at runtime from optional filter parameters — only the conditions that are actually provided get added to the WHERE clause.

// CityService#search
BooleanBuilder predicate = new BooleanBuilder();

if (filter.name() != null)         predicate.and(city.name.containsIgnoreCase(filter.name()));
if (filter.state() != null)        predicate.and(city.state.equalsIgnoreCase(filter.state()));
if (filter.minPopulation() != null) predicate.and(city.population.goe(filter.minPopulation()));
if (filter.maxPopulation() != null) predicate.and(city.population.loe(filter.maxPopulation()));

return cityRepository.findAll(predicate, pageable);

Endpoint: GET /cities?name=ber&minPopulation=500000&hasAirport=true


2. Join with filter

Inner-join a related collection and filter on a property of the joined entity.

// CityRepositoryCustomImpl#findCitiesWithPlaygroundPark
return queryFactory
    .selectFrom(city)
    .distinct()
    .innerJoin(city.parks, park)
    .where(park.hasPlayground.isTrue())
    .fetch();

Endpoint: GET /cities/with-playground-park


3. Aggregation, groupBy, and having

Group results, aggregate across related entities, and filter groups with a HAVING threshold.

// CityRepositoryCustomImpl#statsByState
return queryFactory
    .select(Projections.constructor(StateStats.class,
        city.state,
        city.countDistinct(),
        city.population.sumLong(),
        hospital.numberOfBeds.avg()))
    .from(city)
    .leftJoin(city.hospitals, hospital)
    .groupBy(city.state)
    .having(city.countDistinct().goe(minCityCount))
    .orderBy(city.state.asc())
    .fetch();

Returns StateStats(state, cityCount, totalPopulation, avgHospitalBeds).

Endpoint: GET /cities/stats-by-state?minCityCount=2


4. DTO projection with Projections.constructor

Select only the columns the caller needs — no full entity load, no N+1.

// CityRepositoryCustomImpl#findCitySummaries
return queryFactory
    .select(Projections.constructor(CitySummary.class,
        city.name, city.state, city.population))
    .from(city)
    .orderBy(city.name.asc())
    .fetch();

Returns CitySummary(name, state, population) instead of the full City entity graph.

Endpoint: GET /cities/summaries


5. Correlated subquery with EXISTS

Use JPAExpressions to write a correlated EXISTS subquery.

// CityRepositoryCustomImpl#findCitiesWithIcuHospital
return queryFactory
    .selectFrom(city)
    .where(
        JPAExpressions.selectOne()
            .from(hospital)
            .where(hospital.city.eq(city).and(hospital.hasICU.isTrue()))
            .exists())
    .fetch();

Endpoint: GET /cities/with-icu-hospital


Project structure

src/main/java/.../querydsl/
├── domain/          # JPA entities — City, Park, Hospital, FireBrigade, Location
├── input/           # Request records — CityInput, CitySearchFilter, …
├── output/          # Response records — CitySummary, StateStats
├── repository/      # CityRepository (QuerydslPredicateExecutor)
│                    # CityRepositoryCustom + CityRepositoryCustomImpl  ← QueryDSL here
├── service/         # CityService — orchestration + BooleanBuilder logic
└── controller/      # CityController — REST endpoints

Q-types (QCity, QPark, …) are generated from JPA entities by the QueryDSL APT processor during mvn generate-sources.

Running locally

compose.yaml starts both the PostGIS database and the application:

docker compose up --build

The app is available at http://localhost:8080. The database is internal to the Compose network and not exposed to the host.

Credentials default to postgres/postgres via .env.docker. To use your own, edit that file and pass it to Compose:

docker compose --env-file .env.docker up --build

Stack

  • Java 25, Spring Boot 4
  • QueryDSL JPA 7.2 (openfeign fork)
  • Spring Data JPA + Hibernate + PostGIS (geospatial GEOGRAPHY POINT)
  • Liquibase migrations, Testcontainers for tests

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages