Spring Data Specification Builder
Spring boot library that provides fluent API to define and compose specifications for data querying.
Table of contents
Quick start
Maven:
<dependency>
<groupId>com.kirekov</groupId>
<artifactId>spring-data-specification-builder</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'com.kirekov:spring-data-specification-builder'
}
Status
Usage
back to table of contents
Basic
Specification<Student> spec = FluentSpecificationBuilder
.combinedWithAnd()
.like("name", "%imo%")
.eq("age", 18)
.build();
List<Student> students = studentRepository.findAll(spec);
It is recommended to use hibernate-jpamodelgen in order to auto generate field names.
The query would like this.
Specification<Student> spec = FluentSpecificationBuilder
.combinedWithAnd()
.like(Student_.NAME, "%imo%")
.eq(Student_.AGE, 18)
.build();
List<Student> students = studentRepository.findAll(spec);
By the way, the library supports type-safe links Attribute<Entity, ?>
. So, the query can be enhanced with type checking.
Specification<Student> spec = FluentSpecificationBuilder
.combinedWithAnd()
.like(Student_.name, "%imo%")
.eq(Student_.age, 18)
.build();
List<Student> students = studentRepository.findAll(spec);
Advanced
back to usage
Architecture
FluentSpecificationBuilder
is the entry point to the library and the only public implementation.
Complex queries
back to advanced
The defined conditions can be applied with either &&
or ||
logical expressions.
final var builderAnd = FluentSpecificaitonBuilder.<Student>combinedWithAnd();
final var builderOr = FluentSpecificaitonBuilder.<Student>combinedWithOr();
More than that, you can invert any condition with not()
. Note that not()
strictly requires condition.
You can't build the specification with unclosed not()
.
final var spec = FluentSpecificationBuilder
.<Student>combinedWithAnd()
.like(Student_.name, "%a%")
.not().eq(Student_.age, 22)
.build();
If you have to provide complex condition that cannot be interpreted with the library, you can use specification()
method.
final var spec = FLuentSpecificationBuilder()
.<Student>combinedWithAnd()
.eq(Student_.age, 20)
.specification((root, query, criteriaBuilder) -> )
.build();
That is also means that you can combine the results of different builders.
final var spec = FluentSpecificationBuilder()
.<Student>combinedWithOr()
.specification(
FluentSpecificationBuilder()
.<Student>combinedWithOr()
.build()
)
.specification(
FluentSpecificationBuilder()
.<Student>combinedWithAnd()
.build()
)
.build();
If you need to specify a field in a child entity, you can use PathFunction
.
final var spec = FluentSpecificationBuilder
.<Student>combinedWithAnd()
.like(Student_.name, "%a%")
.not().eq(root -> root.get(Student_.university).get(University_.name), "MIT")
.buildDistinct();