Transform your Spring Data JPA queries from complex boilerplate into elegant, type-safe search operations with Query By Example. This project demonstrates how to implement dynamic, flexible queries without the overhead of writing multiple repository methods or complex JPQL statements.
Query By Example (QBE) is a user-friendly querying technique that allows you to create dynamic queries using domain object instances as templates. This approach shines when building search functionality with multiple optional parameters, such as advanced search forms or dynamic filters.
- Java 23
- Spring Boot 3.3.5
- PostgreSQL
- Docker (for running the database)
- Maven
- Dynamic query generation using domain objects
- Type-safe query construction
- Minimal boilerplate code
- Integration with Spring Data JPA
- Docker-based development environment
- Comprehensive test coverage using TestContainers
Ensure you have the following installed:
- Java 23 JDK
- Docker Desktop
- Maven
- Build and run the application:
./mvnw spring-boot:runThe application will be available at http://localhost:8080
Here's a simple example of how to use Query By Example:
// Create a probe (example) entity
Employee probe = new Employee();
probe.setDepartment("IT");
probe.setPosition("Developer");
// Create the Example with the probe
Example<Employee> example = Example.of(probe);
// Find all matching employees
List<Employee> developers = employeeRepository.findAll(example);For more complex scenarios, you can customize the matching behavior:
// Create a custom ExampleMatcher
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(StringMatcher.CONTAINING);
Employee probe = new Employee();
probe.setDepartment("eng"); // Will match "Engineering"
Example<Employee> example = Example.of(probe, matcher);
List<Employee> engineers = employeeRepository.findAll(example);QBE is ideal for:
- ✅ Search forms with multiple optional filters
- ✅ Quick prototyping and development
- ✅ Simple equality-based queries
- ✅ Scenarios where search criteria are unknown at compile time
Consider alternatives when you need:
- ❌ Complex comparisons (>, <, BETWEEN)
- ❌ OR conditions
- ❌ Complex JOIN operations
- ❌ Custom SQL functions
src/
├── main/
│ ├── java/
│ │ └── dev/danvega/qbe/
│ │ ├── model/
│ │ ├── repository/
│ │ └── service/
│ └── resources/
│ ├── application.yml
│ └── data.sql
└── test/
└── java/
└── dev/danvega/qbe/
The application's main configuration is in application.yml:
spring:
application:
name: qbe
jpa:
show-sql: true
hibernate:
ddl-auto: create-dropThe project uses TestContainers for integration testing, ensuring that tests run against a real PostgreSQL database:
@SpringBootTest
@Testcontainers
class EmployeeRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
DockerImageName.parse("postgres:16-alpine"));
@Autowired
private EmployeeRepository employeeRepository;
// ...
}