diff --git a/src/main/java/com/ainigma100/departmentapi/repository/DepartmentRepository.java b/src/main/java/com/ainigma100/departmentapi/repository/DepartmentRepository.java index b758e58..54f20c3 100644 --- a/src/main/java/com/ainigma100/departmentapi/repository/DepartmentRepository.java +++ b/src/main/java/com/ainigma100/departmentapi/repository/DepartmentRepository.java @@ -5,19 +5,20 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; // << add this import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -public interface DepartmentRepository extends JpaRepository { +public interface DepartmentRepository extends JpaRepository, JpaSpecificationExecutor { Department findByDepartmentCode(String departmentCode); @Query(value = """ select dep from Department dep - where ( :#{#criteria.departmentCode} IS NULL OR LOWER(dep.departmentCode) LIKE LOWER( CONCAT(:#{#criteria.departmentCode}, '%') ) ) - and ( :#{#criteria.departmentName} IS NULL OR LOWER(dep.departmentName) LIKE LOWER( CONCAT(:#{#criteria.departmentName}, '%') ) ) - and ( :#{#criteria.departmentDescription} IS NULL OR LOWER(dep.departmentDescription) LIKE LOWER( CONCAT('%', :#{#criteria.departmentDescription}, '%') ) ) + where ( :#{#criteria.departmentCode} IS NULL OR LOWER(dep.departmentCode) LIKE LOWER(CONCAT(:#{#criteria.departmentCode}, '%')) ) + and ( :#{#criteria.departmentName} IS NULL OR LOWER(dep.departmentName) LIKE LOWER(CONCAT(:#{#criteria.departmentName}, '%')) ) + and ( :#{#criteria.departmentDescription} IS NULL OR LOWER(dep.departmentDescription) LIKE LOWER(CONCAT('%', :#{#criteria.departmentDescription}, '%')) ) """) Page getAllDepartmentsUsingPagination( @Param("criteria") DepartmentSearchCriteriaDTO departmentSearchCriteriaDTO, diff --git a/src/main/java/com/ainigma100/departmentapi/service/impl/DepartmentServiceImpl.java b/src/main/java/com/ainigma100/departmentapi/service/impl/DepartmentServiceImpl.java index 356fab8..1539e5d 100644 --- a/src/main/java/com/ainigma100/departmentapi/service/impl/DepartmentServiceImpl.java +++ b/src/main/java/com/ainigma100/departmentapi/service/impl/DepartmentServiceImpl.java @@ -44,21 +44,27 @@ public DepartmentDTO createDepartment(DepartmentDTO departmentDTO) { } @Override - public Page getAllDepartmentsUsingPagination( - DepartmentSearchCriteriaDTO departmentSearchCriteriaDTO) { + public Page getAllDepartmentsUsingPagination(DepartmentSearchCriteriaDTO departmentSearchCriteriaDTO) { - - Integer page = departmentSearchCriteriaDTO.getPage(); - Integer size = departmentSearchCriteriaDTO.getSize(); + // defensive defaults + Integer page = departmentSearchCriteriaDTO.getPage() == null ? 0 : departmentSearchCriteriaDTO.getPage(); + Integer size = departmentSearchCriteriaDTO.getSize() == null ? 10 : departmentSearchCriteriaDTO.getSize(); List sortList = departmentSearchCriteriaDTO.getSortList(); - // this pageable will be used for the pagination. + // create Pageable using existing utility (keeps your sorting logic) Pageable pageable = Utils.createPageableBasedOnPageAndSizeAndSorting(sortList, page, size); - Page recordsFromDb = departmentRepository.getAllDepartmentsUsingPagination(departmentSearchCriteriaDTO, pageable); + // Build Specification from criteria + var spec = com.ainigma100.departmentapi.specification.DepartmentSpecification.fromCriteria(departmentSearchCriteriaDTO); + + // Query repository using Specification + Pageable + Page recordsFromDb = + departmentRepository.findAll(spec, pageable); + // Map entity list to DTO list List result = departmentMapper.departmentToDepartmentDto(recordsFromDb.getContent()); + // Return a page preserving the paging metadata return new PageImpl<>(result, pageable, recordsFromDb.getTotalElements()); } diff --git a/src/main/java/com/ainigma100/departmentapi/specification/DepartmentSpecification.java b/src/main/java/com/ainigma100/departmentapi/specification/DepartmentSpecification.java new file mode 100644 index 0000000..d242e49 --- /dev/null +++ b/src/main/java/com/ainigma100/departmentapi/specification/DepartmentSpecification.java @@ -0,0 +1,51 @@ +package com.ainigma100.departmentapi.specification; + +import com.ainigma100.departmentapi.dto.DepartmentSearchCriteriaDTO; +import com.ainigma100.departmentapi.entity.Department; +import jakarta.persistence.criteria.*; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Build a Specification from DepartmentSearchCriteriaDTO. + * - departmentCode: prefix match (case-insensitive) + * - departmentName: contains match (case-insensitive) + * - departmentDescription: contains match (case-insensitive) + * + * Extend this class if you want more operators (equals, ranges, enums, etc). + */ +public final class DepartmentSpecification { + + private DepartmentSpecification() {} + + public static Specification fromCriteria(DepartmentSearchCriteriaDTO criteria) { + return (Root root, CriteriaQuery query, CriteriaBuilder cb) -> { + List predicates = new ArrayList<>(); + + if (criteria == null) { + return cb.conjunction(); + } + + // departmentCode: prefix match (deptCode starts with value), case-insensitive + if (StringUtils.hasText(criteria.getDepartmentCode())) { + predicates.add(cb.like(cb.lower(root.get("departmentCode")), criteria.getDepartmentCode().toLowerCase() + "%")); + } + + // departmentName: contains, case-insensitive + if (StringUtils.hasText(criteria.getDepartmentName())) { + predicates.add(cb.like(cb.lower(root.get("departmentName")), "%" + criteria.getDepartmentName().toLowerCase() + "%")); + } + + // departmentDescription: contains, case-insensitive + if (StringUtils.hasText(criteria.getDepartmentDescription())) { + predicates.add(cb.like(cb.lower(root.get("departmentDescription")), "%" + criteria.getDepartmentDescription().toLowerCase() + "%")); + } + + // Combine predicates (AND) + return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[0])); + }; + } +}