스프링부트 검색 기능 (이제 Pagination 을 곁들인)

Yunny.Log ·2022년 8월 9일
0

Spring Boot

목록 보기
73/80
post-thumbnail

나는 페이징 유형을 2개 사용하고 있다. (queryDsl 유무)

1. QueryDSL 사용 X

BEFORE

  • Pageable 이라는 기본 아이를 통해 size, page , sort 정도로만 pagination을 진행하고 있었다.
  • 그러나 이제 , 넘어온 검색어가 프로젝트의 이름, 개발 유형, 생산 조직, 고객사, 차종, 고객 제품번호, 제품번호, 제품명, Phase , Status ^^ 들에 포함되어있는 아이들만 따로 데려와서 pagination을 해줘야 하지! 크하하

    public Page<ProjectDashboardDto> pagingProject(@PageableDefault(size=5)
                                                @SortDefault.SortDefaults({
                                                        @SortDefault(
                                                                sort = "createdAt",
                                                                direction = Sort.Direction.DESC)
                                                })
                                                        Pageable pageRequest,
                                                   ProjectMemberRequest req) {



        Page<ProjectDashboardDto> projectDashboardDtos = projectService.readPageAll(pageRequest, req);


        return projectDashboardDtos;
    }
    

AFTER

  • RequestParam으로 name 이라는 아이를 전달받게 해주었다.
    @CrossOrigin(origins = "https://naughty-raman-7e7eb1.netlify.app")
    @GetMapping("/project/page")
    @AssignMemberId
    public Page<ProjectDashboardDto> pagingProject(
            @PageableDefault(size=5)
            @SortDefault.SortDefaults({
                    @SortDefault(
                            sort = "createdAt",
                            direction = Sort.Direction.DESC)
            })
                    Pageable pageRequest,
            ProjectMemberRequest req,
            @RequestParam(value = "search", required = false) String search) {

        if(search==null) { // 만약 넘겨져온 검색어가 없다면 그냥 다 가져와
            return projectService.readPageAll(pageRequest, req);

        }else{ // 검색어가 존재한다면 검색어 사용해서 검색 
            return projectService.readPageAllSearch(pageRequest, req,name );

        }
    }

  • 그리고 프로젝트 Service 에서 readPageAllSearch 라는 메소드를 설정해주었다.

ProjectService.java

        Page<Project> projectListBefore = 
        projectRepository.
        findByNameContainingIgnoreCaseOrProjectNumberContainingIgnoreCaseOrLifecycleContainingIgnoreCaseOrClientItemNumberContainingIgnoreCase(
                search,
                search,
                search,
                search,
                pageRequest
        );

ProjectRepository.java

    Page<Project> 
    findByNameContainingIgnoreCaseOrProjectNumberContainingIgnoreCaseOrLifecycleContainingIgnoreCaseOrClientItemNumberContainingIgnoreCase
            (String name,
             String projectNumber,
             String lifecycle,
             String clientItemNumber,
             Pageable pageable);
  • 이는 프로젝트 레포지토리에서 findByNameContainingIgnoreCaseOrProjectNumberContainingIgnoreCaseOrLifecycleContainingIgnoreCaseOrClientItem 라는 JPA 문을 작성해주었다.
  • 좀 긴데, 프로젝트 안에서 String 으로 된 아이들을 다 검사해주기 위해서 request param 으로 넘어온 search 라는 단어로
    프로젝트의 속성들을
  • Containing : 포함하는 지
  • IgnoreCase : 대소문자 구별 x
findByNameContainingIgnoreCase // name 이라는 string 으로 넘겨져온 아이가 프로젝트의 이름에 존재하는지 (대소문자 구별 X)
Or // 혹은 
ProjectNumberContainingIgnoreCase
Or
LifecycleContainingIgnoreCase
Or
ClientItemItemNumberContainingIgnoreCase 


2. QueryDSL 사용 O

BEFORE

ReadCondition.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProduceOrganizationReadCondition {

    @NotNull(message = "페이지 번호를 입력해주세요.")
    @PositiveOrZero(message = "올바른 페이지 번호를 입력해주세요. (0 이상)")
    private Integer page;

    @NotNull(message = "페이지 크기를 입력해주세요.")
    @Positive(message = "올바른 페이지 크기를 입력해주세요. (1 이상)")
    private Integer size;


}

RepositoryImpl.java

@Transactional(readOnly = true)
public class CustomProduceOrganizationRepositoryImpl extends QuerydslRepositorySupport implements CustomProduceOrganizationRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public CustomProduceOrganizationRepositoryImpl(JPAQueryFactory jpaQueryFactory) {  
        super(ProjectType.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public Page<ProduceOrganizationReadResponse> findAllByCondition(ProduceOrganizationReadCondition cond) {
        Pageable pageable = PageRequest.of(cond.getPage(), cond.getSize());
        Predicate predicate = createPredicate(cond);
        return new PageImpl<>(fetchAll(predicate, pageable), pageable, fetchCount(predicate));
    }


    private List<ProduceOrganizationReadResponse> fetchAll(Predicate predicate, Pageable pageable) { 
        return getQuerydsl().applyPagination(
                pageable,
                jpaQueryFactory
                        .select(constructor(
                                ProduceOrganizationReadResponse.class,
                                produceOrganization.id,
                                produceOrganization.name
                        ))
                        .from(produceOrganization)
                        .where(predicate)
                        .orderBy(produceOrganization.id.desc())
        ).fetch();
    }

    private Long fetchCount(Predicate predicate) { 
        return jpaQueryFactory.select(
                        produceOrganization.count()
                ).from(produceOrganization).
                where(predicate).fetchOne();
    }

    private Predicate createPredicate(ProduceOrganizationReadCondition cond) {  
        return new BooleanBuilder();  # 따로 뭐가 없어
    }


    private <T> Predicate orConditions(List<T> values, Function<T, BooleanExpression> term) {  
        return values.stream()
                .map(term)
                .reduce(BooleanExpression::or)
                .orElse(null);
    }
}
  • createPredicate 가 현재는 따로 없다, 다른 조건 없이 딱 기본 page, size 만 지금 주기 때문
    private Predicate createPredicate(ProduceOrganizationReadCondition cond) {  
        return new BooleanBuilder();  # 따로 뭐가 없어
    }

AFTER

  • ReadCondition 에 이제 생산조직을 name 으로도 검색 가능하도록 이를 추가

검색어가 생산조직의 이름에 포함 되어있다면 (contatins) 나타나게 해주기

ReadCondition.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProduceOrganizationReadCondition {

    @NotNull(message = "페이지 번호를 입력해주세요.")
    @PositiveOrZero(message = "올바른 페이지 번호를 입력해주세요. (0 이상)")
    private Integer page;

    @NotNull(message = "페이지 크기를 입력해주세요.")
    @Positive(message = "올바른 페이지 크기를 입력해주세요. (1 이상)")
    private Integer size;

    private String name; # 이러한 검색 조건 하나를 추가  ! 

}

RepositoryImpl.java

  • 바뀐 부분은 predicate 만들어지는 부분 !


// 읽기 조건에서 얻어온 이름 참 거짓 유무 판별
     private Predicate createPredicate(ProduceOrganizationReadCondition cond) {  
        return new BooleanBuilder()
        .and(orConditionsByEqNames(cond.getName()));  // 포함되어있다면 그에 따른 predicate 생성 
        
    }



//검색어로 넘어온 단어가 이름에 contain (포함) 되어있는지 판별 
    private Predicate orConditionsByEqNames(String word) {
        List<String> words = new ArrayList<>();
        words.add(word);
        return orConditions(words, produceOrganization.name::contains); 
        
    }
 
 
// 전달받은 value들을 OR 연산으로 묶어서 반환 (지금은) 
    private <T> Predicate orConditions(List<T> values, Function<T, BooleanExpression> term) {
        return values.stream()
                .map(term)
                .reduce(BooleanExpression::or)
                .orElse(null);
    }

 

0개의 댓글