Today was my first time with criteria Api. i have create an application in Spring boot in order to make server side pagination with multiple key of an entity filtering. So in my case i have created an entity called User and i started to implement code to paginate the data but with Criteria API.
After implementing the pagination without filtering and Criteria Api everything worked perfectly! every page return 8 results and it is well organized by current page, totalPages, etc ..
But later i have decided to start to implement Criteria API by searching my entity username and userRole. my goal is to make that paging i did in the last step mixed with filtering of keys. In case that my keys are empty then paginate else paginate and filter. So after implementing i have discouvered that filtering works perfectly but pagination do not work correctly anymore because i am receiving all the results in every page. that problem happened only after implementing Criteria API which i just discovered today.
I am trying to reach my goal by keeping all i spoke about in one query and paginate correctly
Here what i have done with my UserCriteriaRepository
@Repository
public class UserCriteriaRepository {
private final EntityManager entityManager;
private final CriteriaBuilder criteriaBuilder;
public UserCriteriaRepository(EntityManager entityManager) {
this.entityManager = entityManager;
this.criteriaBuilder = entityManager.getCriteriaBuilder();
}
public ResponsePagingAndSorting<UserDTO> findAllWithFilters(int page, int size, String username, String userRole) {
CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
Root<User> userRoot = criteriaQuery.from(User.class);
Predicate predicate = getPredicate(username,userRole, userRoot);
criteriaQuery.where(predicate);
TypedQuery<User> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setMaxResults(size * 10);
long usersCount = getUsersCount(predicate);
int totalPages = (int) ((usersCount / size) 1);
List<User> userList = new ArrayList<>();
userList = typedQuery.getResultList();
List<UserDTO> userDTOList = UserMapper.toListDTO(userList);
return new ResponsePagingAndSorting<UserDTO>("Users List ",200,userDTOList,page,
usersCount, totalPages);
}
private Predicate getPredicate(String username, String userRole,
Root<User> userRoot) {
List<Predicate> predicates = new ArrayList<>();
if(Objects.nonNull(username)){
predicates.add(
criteriaBuilder.like(userRoot.get("username"),
"%" username "%")
);
}
if(Objects.nonNull(userRole)){
UserRoleType userRoleType = null;
switch (userRole){
case "MEMBER": userRoleType = UserRoleType.MEMBER;
break;
case "ADMIN": userRoleType = UserRoleType.ADMIN;
break;
case "SUPER_ADMIN": userRoleType = UserRoleType.SUPER_ADMIN;
break;
}
if (userRoleType != null) {
predicates.add(
criteriaBuilder.equal(userRoot.get("userRole"),
userRoleType)
);
}
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
private Pageable getPageable(int page, int size) {
return PageRequest.of(page,size);
}
private long getUsersCount(Predicate predicate) {
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<User> countRoot = countQuery.from(User.class);
countQuery.select(criteriaBuilder.count(countRoot)).where(predicate);
return entityManager.createQuery(countQuery).getSingleResult();
}
}
My Service:
//paging with Criteria Api
@Override
public ResponsePagingAndSorting<UserDTO> getAllUsers(int page, int size ,String username, String userRole) {
ResponsePagingAndSorting<UserDTO> response = userCriteriaRepository.findAllWithFilters(page,size, username, userRole);
return response;
}
My Controller
@GetMapping("/get/all")
@ResponseBody
public ResponsePagingAndSorting<UserDTO> getAllUsers(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "8") int size,@RequestParam(defaultValue = "") String username,
@RequestParam(defaultValue = "") String userRole) {
ResponsePagingAndSorting<UserDTO> response = userService.getAllUsers(page,size,username,userRole);
log.warn("Response controller is " response);
return response;
}
My ResponsePagingAndSorting dto object:
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ResponsePagingAndSorting<T> {
String message;
int status_code;
List<T> body = new ArrayList<>();
int currentPage;
long totalItems;
int totalPages;
}
In Database i have in total of 17 users, so in postman i see all the 17 everytime but if i search by username or userRole or both it works? why pagination works only when i user the filters? Can not i paginate data without seraching by username or userRole? what is wrong with my code ??? how to make pagination works correctly with the filtering enabled or disabled? Why if
Postman screen capture: unfortunately all results are displayed in page 0
Screen Capture pagination username filter: works correctly
i hope that i will find a solution
CodePudding user response:
Problem Solved by using JpaSpecification
here the Specification class:
@Component
public class UserSpecification {
public Specification<User> getUsers(String username, String userRole) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (username != null && !username.isEmpty()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("username")),
"%" username.toLowerCase() "%"));
}
if (userRole != null && !userRole.isEmpty()) {
UserRoleType userRoleType = null;
switch (userRole) {
case "MEMBER": userRoleType = UserRoleType.MEMBER;
break;
case "ADMIN": userRoleType = UserRoleType.ADMIN;
break;
case "SUPER_ADMIN": userRoleType = UserRoleType.SUPER_ADMIN;
break;
}
predicates.add(criteriaBuilder.equal(root.get("userRole"), userRoleType));
}
query.orderBy(criteriaBuilder.asc(root.get("username")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}