SpecMapper
SpecMapper
is the most important class and serves as the API entry point for all specification operations:
var mapepr = SpecMapper.builder().build();
Next, we define a POJO (Plain Old Java Object) that encapsulates the query conditions, such as:
@Data
public class CustomerCriteria {
@Spec(Like.class)
String firstname;
}
With this, we can perform the conversion to a Specification
. Once we have the Specification
, we can query the database using the original approach, for example, through the repository of Spring Data JPA:
var criteria = new CustomerCriteria();
criteria.setFirstname("Hello")
var mapper = SpecMapper.builder().build();
var specification = mapper.toSpec(criteria);
customerRepository.findAll(specification);
The executed SQL will be like:
... where x.firstname like '%Hello%'
Skipping Strategy
In the fields of the POJO, if any of the following conditions are met, they will be ignored during the conversion process:
- No Spec Annotation is attached.
- The value is null.
- If the type is Iterable and the value is empty.
- If the type is Optional and the value is empty.
- If the type is CharSequence and the length of value is 0.
- If the type is Array and the length of value is 0.
- If the type is Map and the value is empty.
For example, after constructing the following POJO, if no values are set and it is directly converted into a Specification
for querying:
@Data
public class CustomerCriteria {
@Spec(Like.class)
String firstname;
String lastname = "Hello";
@Spec
String nickname = "";
@Spec(GreaterThat.class)
Optional<Integer> age = Optional.empty();
@Spec(In.class)
Collection<String> addresses = Arrays.asList();
}
var mapper = SpecMapper.builder().build();
customerRepository.findAll(mapper.toSpec(new CustomerCriteria()));
The executed SQL in the above example will not have any filtering conditions.
If you are using the Builder Pattern (e.g., Lombok’s @Builder), please pay special attention to the default values set in the builder.
If you want to customize the logic for skipping, you can implement a SkippingStrategy
and pass it when constructing a SpecMapper
:
var mapper = SpecMapper.builder()
.defaultResolvers()
.skippingStrategy(fieldValue -> {
// Determine whether to skip the field value and return a boolean
})
.build();