Skip to content

HarryJhin/querydsl-ktx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

134 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

querydsl-ktx

CI Maven Central Kotlin Spring Boot License

한국어 | Documentation

Eliminate 90% of BooleanBuilder boilerplate in QueryDSL dynamic queries with null-safe infix Kotlin extensions.

The Problem

If you've used QueryDSL in Kotlin, you've written this pattern hundreds of times:

val builder = BooleanBuilder()
if (name != null) builder.and(member.name.contains(name))
if (status != null) builder.and(member.status.eq(status))
if (minAge != null && maxAge != null) builder.and(member.age.between(minAge, maxAge))
else if (minAge != null) builder.and(member.age.goe(minAge))
else if (maxAge != null) builder.and(member.age.loe(maxAge))
if (startDate != null && endDate != null) builder.and(member.createdAt.between(startDate, endDate))
else if (startDate != null) builder.and(member.createdAt.goe(startDate))
else if (endDate != null) builder.and(member.createdAt.loe(endDate))

Every optional filter adds 1-3 lines. Range filters need 3 branches. The pattern is always the same.

The Solution

selectFrom(member)
    .where(
        member.name contains name,
        member.status eq status,
        member.age between (minAge to maxAge),
        member.createdAt between (startDate to endDate),
    )
    .page(pageable)

Null parameters are automatically skipped. between with a Pair handles one-sided ranges. 30 lines to 10 lines.

Quick Start

// 1. Add the dependency
implementation("io.github.harryjhin:querydsl-ktx-spring-boot-starter:1.2.0")
// 2. Extend QuerydslRepository
@Repository
class MemberRepository : QuerydslRepository<Member>() {

    private val member = QMember.member

    fun search(name: String?, status: String?, pageable: Pageable): Page<Member> =
        selectFrom(member)
            .where(
                member.name contains name,
                member.status eq status,
            )
            .page(pageable)
}

Why querydsl-ktx?

Approach Drawback
BooleanBuilder Verbose, error-prone with ranges
Per-field helpers (statusEq()) Duplicated per entity, partial coverage
Specification Separate from QueryDSL, no infix syntax
Top-level extensions Global scope pollution, name clashes
querydsl-ktx Standard, tested, complete

Extensions are scoped through interface implementation -- no global namespace pollution. Learn more

Features

  • Null-safe dynamic queries -- entity.status eq null returns null (skipped), no if check needed. Replaces both BooleanBuilder and per-field helper functions.
  • One-sided range survival -- entity.date between (from to null) becomes date >= from. A single expression handles 4 combinations that used to need 3-branch if/else.
  • Pagination without fetchResults() -- fetchResults() is deprecated since QueryDSL 5.0. page() auto-generates count queries for simple cases and accepts a lambda for complex ones. slice() and exactSlice() avoid count queries entirely.
  • Type-safe dynamic sorting -- SortSpec provides a whitelist mapping for Sort property names. No more ?sort=password,asc security holes or broken join-column sorting.
  • 8 extension interfaces -- null-safe infix operators for Boolean, Simple, Comparable, Number, String, Temporal, Collection, SubQuery expressions.
  • Subquery / collection comparisons -- eqAll, gtAny, loeAll and other ALL/ANY variants with consistent null-skip semantics for both CollectionExpression and SubQueryExpression arguments.
  • Reified expression templates -- numberTemplate<Float>(...) instead of Expressions.numberTemplate(Float::class.java, ...).
  • Case/When DSL -- case<Int> { when(pred) then value; otherwise(default) } with null-safe branches.
  • Bulk DML -- modifying { } with auto flush/clear.
  • Multi-datasource ready -- auto-config selects the @Primary EntityManagerFactory when multiple data sources are present. Non-primary factories require manual JPAQueryFactory registration.
  • GraalVM native image -- RuntimeHints auto-registered via @ImportRuntimeHints for out-of-the-box native image support.

Documentation

Installation Gradle, Maven setup and module selection
Quick Start Write your first dynamic query in 5 minutes
User Guide Dynamic queries, extensions, expressions, Case/When DSL, pagination, bulk DML
API Reference Dokka-generated API documentation

Requirements

Dependency Version Note
Spring Boot 3.0+ CI tests 3.0, 3.2, 3.3, 3.4.
QueryDSL 5.1.0+
Kotlin 1.7+
Java 17+

For LLMs

This project provides an llms.txt for AI context via GitMCP.

Claude Code

claude mcp add querydsl-ktx -- mcp-remote https://gitmcp.io/HarryJhin/querydsl-ktx

Codex

codex mcp add querydsl-ktx -- mcp-remote https://gitmcp.io/HarryJhin/querydsl-ktx

Gemini CLI

gemini mcp add querydsl-ktx -- mcp-remote https://gitmcp.io/HarryJhin/querydsl-ktx

JSON config (manual)

{
  "mcpServers": {
    "querydsl-ktx": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/mcp-remote@latest", "https://gitmcp.io/HarryJhin/querydsl-ktx/sse"]
    }
  }
}

License

Apache License 2.0

About

Null-safe infix Kotlin extensions for QueryDSL dynamic queries

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages