I have often been asked to develop advanced search services. By advanced search, I mean searches in which it’s possible to apply multiple filters on all (or almost all) fields such as: like, between, in, greater than, etc. So imagine having to build a service, based on one or more entities, capable of offering an endpoint that can be called like this (start keeping an eye out for special suffixes ): <propertyName>< > _suffix curl - request GET \ - url 'http://www.myexampledomain.com/persons? firstName=Biagio &lastName_startsWith=Toz &birthDate_gte=19910101 &country_in=IT,FR,DE &company.name_in=Microsoft,Apple &company.employees_between=500,5000' or curl --request GET \ --url 'http://www.myexampledomain.com/persons? firstName_endsWith=gio &lastName_in=Tozzi,Totti &birthDate_lt=19980101 &_offset=0 &_limit=100 &birthDate_sort=ASC' If you are using in a Spring Boot project, you can now develop this search service with just a few lines of code thanks to ! Let me explain what it is. JPA JPA Search Helper JPA Search Helper First step: @Searchable annotation Start by applying the annotation to the fields in your DTO, or your JPA entity, that you want to make available for search. @Searchable @Data public class Person { @Searchable private String firstName; @Searchable private String lastName; @Searchable(entityFieldKey = "dateOfBirth") private Date birthDate; @Searchable private String country; private Company company; @Data public static class Company { @Searchable(entityFieldKey=companyEntity.name) private String name; @Searchable(entityFieldKey=companyEntity.employeesCount) private int employees; } } The annotation allows you to specify: Core properties: : the name of the field defined on the entity bean (not to be specified if using the annotation on the entity bean). If not specified the key will be the field name. entityFieldKey : the managed object type by entity. If not specified the librariy tries to obtain it based on field type (es. field without target type definition will be INTEGER). If there is no type compatible with those managed, it will be managed as a string. Managed types: STRING, INTEGER, DOUBLE, FLOAT, LONG, BIGDECIMAL, BOOLEAN, DATE, LOCALDATE, LOCALDATETIME, LOCALTIME, OFFSETDATETIME, OFFSETTIME. targetType Integer Validation properties: : only for DATE target type. Defines the date pattern to use. datePattern , : maximum/minimum length of the value maxSize minSize , : only for numeric types. Maximum/minimum number of digits. maxDigits minDigits : regex pattern. regexPattern : only for decimal numeric types. Default #.## decimalFormat Continuing the example, our entity classes: @Entity @Data public class PersonEntity { @Id private Long id; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "BIRTH_DATE") private Date dateOfBirth; @Column(name = "COUNTRY") private String country; @OneToOne private CompanyEntity companyEntity; } @Entity @Data public class CompanyEntity { @Id private Long id; @Column(name = "NAME") private String name; @Column(name = "COUNT") private Integer employeesCount; } Second and last step: JPASearchRepository<?> Your Spring JPA repository must extend : JPASearchRepository<?> @Repository public interface PersonRepository extends JpaRepository<PersonEntity, Long>, JPASearchRepository<PersonEntity> { } Well, let’s build the filters and feed them to the repository: // ... Map<String, String> filters = new HashMap<>(); filters.put("firstName_eq", "Biagio"); filters.put("lastName_startsWith", "Toz"); filters.put("birthDate_gte", "19910101"); filters.put("country_in", "IT,FR,DE"); filters.put("company.name_in", "Microsoft,Apple"); filters.put("company.employees_between", "500,5000"); // Without pagination List<PersonEntity> fullSearch = personRepository.findAll(filters, Person.class); filters.put("birthDate_sort" : "ASC"); filters.put("_limit", "10"); filters.put("_offset", "0"); // With pagination Page<PersonEntity> sortedAndPaginatedSearch = personRepository.findAllWithPaginationAndSorting(filters, Person.class); // ... You just need to define a map whose key is made up of and search value. The complete list of suffixes, i.e. available filters, is . <fieldName>< > _suffix here : if no suffix is specified the search is done in equal (_eq) Note 1 : In the example I applied the @Searchable annotation on the DTO fields. Alternatively, it’s possible to apply them directly on the entity. Note 2 A pseudo-real implementation in a Spring Boot project Service/Manager bean: @Service public class PersonManager { @Autowired private PersonRepository personRepository; public List<Person> find(Map<String, String> filters) { return personRepository.findAllWithPaginationAndSorting(filters, Person.class).stream().map(this::toDTO).toList(); } private static Person toDTO(PersonEntity personEntity) { // ... } } Controller: @RestController public class MyController { @Autowired private PersonManager personManager; @GetMapping(path="/persons", produces = MediaType.APPLICATION_JSON_VALUE) public List<Person> findPersons(@RequestParam Map<String, String> requestParams) { return personManager.find(requestParams); } } ..et voilà les jeux sont faits Extra The library allows you to force join fetch. A join allows associations or collections of values to be initialized along with their parent objects using a single select. “fetch” That’s how: // ... Map<String, JoinFetch> fetches = Map.of("companyEntity", JoinFetch.LEFT); personRepository.findAll(filters, Person.class, fetches); // ... That’s all.. for now! Also published . here