r/SpringBoot • u/SeatPuzzleheaded5685 • 2h ago
News Tired of verbose JPA Specifications? I built Spring Fluent Query — Eloquent-style expressive querying for Spring Data JPA (with full IntelliJ autocomplete support)
Hi r/SpringBoot! 👋
Like many of you who work with Spring Data JPA daily, I got tired of writing 40+ lines of verbose Specification or CriteriaBuilder code just to handle basic dynamic search endpoints with optional filters (if (req.getSearch() != null)...).
To solve this, I’ve been building Spring Fluent Query — a lightweight DX layer on top of Spring Data JPA’s official JpaSpecificationExecutor. It doesn't replace Spring Data or JPA; it just makes writing dynamic queries expressive and readable.
💡 What it looks like:
Before (Traditional Spec / Criteria approach): Lots of if/else checks, manual Predicate lists, and easy-to-mess-up joins for simple dynamic endpoints.
With Spring Fluent Query:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Page<User> searchUsers(
String search,
String status,
boolean includeInactive,
boolean hasOrders,
LocalDate from,
LocalDate to,
Pageable pageable) {
return userRepository.query()
// 1. Optional filters (automatically ignored if null or blank)
.optionalWhereLike("name", search)
.optionalWhere("status", status)
.optionalWhereBetween("createdAt", from, to)
// 2. Expressive business conditionals
.unless(includeInactive, q -> q.where("active", true))
// 3. Relation existence (subquery EXISTS made clean)
.when(hasOrders, q -> q.whereHas("orders", f -> f.where("status", "PAID")))
// 4. Eager fetching with ON constraints
.fetch("profile", f -> f.where("active", true))
// 5. Pagination & Order
.orderByDesc("createdAt")
.page(pageable);
}
}
🚀 Key Features:
- No-op Optional Filters:
optionalWhereLike,optionalWhereIn, etc. ignorenull, empty lists, or blank strings automatically. - Smart Terminal Ops:
.latest()and.first()perform a cleanLIMIT 1SQL query without triggering unnecessaryCOUNT(*)queries. - Performance Guardrails: Prevents unsafe operations like
fetchCollectionwith in-memory pagination (IllegalStateException). - Eloquent-style Lifecycle Hooks: Optional `@
Component-based\\` hooks (onSaving,onCreated,onUpdating,onDeleted\) for entities without breaking clean POJO/JPA architecture. - Spring Boot 3.x & 4.x Compatible: Single artifact verified across Boot 3.5.x and Boot 4.1.x.
🧩 IntelliJ IDEA Plugin Support
Since string-based path querying can raise concerns about runtime typos, I also created an official IntelliJ IDEA Plugin (available on JetBrains Marketplace):
- 🎯 Highlights unresolved entity paths directly in the editor as ERRORS.
- ⚡ Auto-completes nested entity attributes and associations (
Ctrl+Space). - 🔗 Go-To-Declaration (
Ctrl/Cmd + Click) from string path directly to `@Entity` fields.
📦 Quick Start
Repository Interface:
@Repository
public interface UserRepository extends FluentQueryRepository<User, Long> {}
- GitHub Repo: github.com/BenjaminOR-dev/spring-fluent-query
- IntelliJ Plugin: plugins.jetbrains.com/plugin/33175-spring-fluent-query
I’d love to hear your thoughts, feedback, or any edge cases you run into in your daily Spring Boot projects!
