An alternative API for filtering data with Spring MVC & Spring Data JPA
An alternative API for filtering data with Spring MVC & Spring Data JPA
An alternative API for filtering data with Spring MVC & Spring Data JPA.
A thorough introduction and the original rationale behind this component can be found my blog: http://blog.kaczmarzyk.net/2014/03/23/alternative-api-for-filtering-data-with-spring-mvc-and-spring-data/. In this file you can find a summary of all the current features and some API examples.
You can also take a look on working Spring Boot apps that use this library:
Equal, Like, GreaterThan etc.and keywordor keyworddeleted = false"abc" sent against an integer field)@PathVariable annotation) in specificationsLocalDateTime, etc.) and the support of defining custom convertersLocale configuration for case-insensitive matching The following HTTP request:
GET http://myhost/api/customers?firstName=Homercan be handled with the following controller method:
@RequestMapping(value = "/customers", params = "firstName")
public Iterable findByFirstName(
@Spec(path = "firstName", spec = Like.class) Specification spec) {
return customerRepo.findAll(spec);
}which will result in the following JPA query:
select c from Customer c where c.firstName like '%Homer%'Alternatively you can annotate an interface:
@Spec(path="firstName", params="name", spec=Like.class)
public interface NameSpec extends Specification {
}and then use it as a controller parameter without any further annotations.
All you need to do is to wire SpecificationArgumentResolver into your application. Then you can use @Spec and other annotations in your controllers. SpecificationArgumentResolver implements Spring's HandlerMethodArgumentResolver and can be plugged in as follows:
@Configuration
@EnableJpaRepositories
public class MyConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List argumentResolvers) {
argumentResolvers.add(new SpecificationArgumentResolver());
}
...
}The library converts HTTP parameters (strings) into most popular Java types such as dates, numbers, and enums. In case of a need for some additional conversion, please see how to configure specification argument resolver with If you need to use additional converters please see custom converters section.
Use @Spec annotation to automatically resolve a Specification argument of your controller method. @Spec has path property that should be used to specify property path of the attribute of an entity, e.g. address.city. By default it's also the name of the expected HTTP parameter, e.g. GET http://myhost?address.city=Springfield.
Use spec attribute of the annotation to specify one of the filtering strategies listed in the subsections below (e.g. Like, Equal, In)
For multi value filters like: In.class, NotIn.class there are two ways of passing multiple arguments. The first way is passing the same HTTP parameter multiple times as follows:
GET http://myhost/customers?gender=MALE&gender=FEMALEThe second way is the use paramSeparator attribute of @Spec, which determines the argument separator (can be specified only for query parameters).
For example the following controller method:
@RequestMapping(value = "/customers", params = "genderIn")
@ResponseBody
public Object findCustomersByGender(
@Spec(path = "gender", params = "genderIn", paramSeparator = ",", spec = In.class) Specification spec) {
return customerRepo.findAll(spec);
}will handle GET http://myhost/customers?gender=MALE,FEMALE in exactly the same way as GET http://myhost/customers?gender=MALE&gender=FEMALE (as one parameter with two values ["MALE","FEMALE"]). Without specifying paramSeparator param gender=MALE,FEMALE will be processed as single value: ["MALE,FEMALE"].
Filters using JPAQL like expression. It adds a wildcard % at the beginning and the end of the actual value, e.g. (..) where firstName like %Homer%.
Usage: @Spec(path="firstName", spec=Like.class).
There are also other variants which apply the wildcard only on the beginning or the ending of the provided value: StartingWith and EndingWith.
The negated version is available: NotLike which executes queries such as (..) where firstName not like %Homer%.
All Like-based specifications support character escaping.
Works as Like, but the query is also case-insensitive.
Usage: @Spec(path="firstName", spec=LikeIgnoreCase.class).
There are also other variants which apply the wildcard only on the beginning or the ending of the provided value: StartingWithIgnoreCase and EndingWithIgnoreCase.
Case-insensitive comparisons are performed using the database's UPPER() function on both sides of the comparison. The results depend on your database's collation settings. Locale settings is important for case-insensitive searches. Please check the Locale support section for details.
A negation for this specification is also available: NotLikeIgnoreCase.
All Like-based specifications support character escaping.
Compares an attribute of an entity with the value of a HTTP parameter (exact match). E.g. (..) where gender = FEMALE.
Supports multiple data types: numbers, booleans, strings, dates, enums.
Usage: @Spec(path="gender", spec=Equal.class).
The default date format used for temporal fields is yyyy-MM-dd. It can be overriden with a configuration parameter (see LessThan below). If for date-time formats which store also time (LocalDateTime, OffsetDateTime, Instant, etc.) only the date is provided, then the default time value (midnight - UTC time for OffsetDateTime) will be used for checking equality. To include all results within particular date (day) use EqualDay specification.
A negation for this specification is also available: NotEqual.
Works as Equal, but the query is also case-insensitive, could be used for fields of type: String, Enum.
Case-insensitive comparisons are performed using the database's UPPER() function on both sides of the comparison. The results depend on your database's collation settings. Locale settings is important for case-insensitive searches. Please check the Locale support section for details.
A negation for this specification is also available: NotEqualIgnoreCase.
Compares an attribute of an entity with multiple values of a HTTP parameter. E.g. (..) where gender in (MALE, FEMALE).
Supports multiple data types: numbers, booleans, strings, dates, enums.
Usage: @Spec(path="gender", spec=In.class).
HTTP request example:
GET http://myhost/customers?gender=MALE&gender=FEMALEor if paramSeparator is specified (eg. @Spec(path="gender", paramSeparator=",", spec=In.class)):
GET http://myhost/customers?gender=MALE,FEMALEThe default date format used for temporal fields is yyyy-MM-dd. It can be overridden with a configuration parameter (see LessThan below).
A negation for this specification is also available: NotIn.
Works as In, but the query is also case-insensitive.
Usage: @Spec(path="firstName", spec=InIgnoreCase.class).
Case-insensitive comparisons are performed using the database's UPPER() function on both sides of the comparison. The results depend on your database's collation settings. Locale settings is important for case-insensitive searches. Please check the Locale support section for details.
A negation for this specification is also available: NotInIgnoreCase.
Filters using is null or is not null, depending on the value of the parameter passed in. A value of true will filter for is null, and a value of false will filter for is not null.
The data type of the field specified in path can be anything, but the HTTP parameter must be a Boolean. You should use params attribute to make it clear that the parameter is filtering for null values.
Usage: @Spec(path="activationDate", params="activationDateNull" spec=Null.class).
If you want the query to be static, i.e. not depend on any HTTP param, you can use IsNull or IsNotNull specifications. Alternatively, you can use Null with constVal attribute of Spec annotation:
For example @Spec(path="nickname", spec=Null.class, constVal="true") will always add nickname is null to the query.
An inversion of Null d
No open issues yet, or sync has not completed.