I'm trying to implement search functionality limited by IN clause:
I want to implement search implementation with filter limitation:
@GetMapping("find")
public Page<MerchantUserDTO> getAllBySpecification(
@And({
@Spec(path = "name", spec = LikeIgnoreCase.class),
@Spec(path = "login", spec = LikeIgnoreCase.class),
@Spec(path = "email", spec = LikeIgnoreCase.class),
}) Specification<Users> specification,
@SortDefault(sort = "login", direction = Sort.Direction.DESC) Pageable pageable
) {
return merchantUserService.getAllBySpecification(specification, pageable)
.map(g -> MerchantUserDTO.builder()
.id(g.getId())
.login(g.getLogin())
.build()
);
}
@Override
public Page<Users> getAllBySpecification(Specification<Users> specification, Pageable pageable) {
return dao.findAllByTypeIn(specification, pageable, "MerchantUser");
}
Repository:
@Repository
public interface MerchantUserRepository extends JpaRepository<Users, Integer>, JpaSpecificationExecutor<Users> {
Page<Users> findAllByTypeIn(Pageable page, String... types);
Page<Users> findAllByTypeIn(Specification<Users> specification, Pageable pageable, String... types);
}
What is the proper way to extend the specification with IN clause?
specification.and(path.in(types))
path is a attribute but how to implement it properly?
Let's follow the Spring Data JPA naming convention to write a query method for the IN clause for the Product entity class. Example: Consider the Product entity class and if we want to retrieve products with In clause then here is the Spring data JPA query method: List<Product> findByNameIn(List<String> names);
If keyword is null, invoke the default findAll() method of the repository interface (provided by Spring Data JPA). As you can see, beside the search result stored in the listProducts object added to the model, the keyword is also added to the model so in the search form we can display the keyword again.
Its findById method retrieves an entity by its id. The return value is Optional<T> . Optional<T> is a container object which may or may not contain a non-null value. If a value is present, isPresent returns true and get returns the value.
Generally this can be achieved this way:
1) Create specification implementation
public class MerchantUserSpecification implements Specification<Users> {
private final List<String> types;
public MerchantUserSpecification(List<String> types) {
this.types = types;
}
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if (types != null && !types.isEmpty()) {
return root.get(Users_.type).in(types);
} else {
// always-true predicate, means that no filtering would be applied
return cb.and();
}
}
2) Use method Page findAll(@Nullable Specification spec, Pageable pageable); inherited from JpaSpecificationExecutor
interface instead of using your custom findAllByTypeIn(Specification<Users> specification....)
@Override
public Page<Users> getAllBySpecification(Specification<Users> specification, Pageable pageable) {
// combine original specification (passed from outside) and filter-by-types specification
Specification<Users> finalSpec = specification
.and(new MerchantUserSpecification(Arrays.asList("MerchantUser")))
return dao.findAll(finalSpec, pageable)
}
P.S.
With Java 8+ and for simple cases (like yours) the code may be reduced even more. Instead of implementing Specification<T>
in separate class you can just create a method
private Specification<Users> typeIn(List<String> types) {
return (root, query, cb) -> {
if (types != null && !types.isEmpty()) {
return root.get(Users_.type).in(types);
} else {
// always-true predicate, means that no filtering would be applied
return cb.and();
}
}
}
@Override
public Page<Users> getAllBySpecification(Specification<Users> specification, Pageable pageable) {
// combine original specification (passed from outside) and filter-by-types specification
Specification<Users> finalSpec = specification
.and(typeIn(Arrays.asList("MerchantUser")))
return dao.findAll(finalSpec, pageable)
}
UPDATE: Shortest way
@Override
public Page<Users> getAllBySpecification(Specification<Users> specification, Pageable pageable) {
// combine original specification (passed from outside) and filter-by-types specification
Specification<Users> finalSpec = specification
.and((root, query, cb) -> root.get(Users_.type).in(Arrays.asList("MerchantUser"))
return dao.findAll(finalSpec, pageable)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With