- 동적 쿼리
- 상황에 따라 내용이 바뀌는 쿼리
- JPA Specification
- 외부 라이브러리를 사용하지 않는 방식
- 기존의 repository 인터페이스에 JpaSpecificationExcutor 상속, 동적쿼리 사용을 위한 클래스 생성
- 사용 1: title에 search contents를 포함하는 값을 모두 들고오기
public static Specification<Post> titleContains(String searchContent) { return (root, query, criteriaBuilder) -> { return criteriaBuilder.like(root.get("title"), "%" + searchContent + "%"); }; }
- 사용 2: subquery 생성하여 join 하여 검색 후 들고오기
public static Specification<Post> tagNameContains(String searchTag) { return (root, query, criteriaBuilder) -> { // 서브쿼리를 생성합니다. Subquery<Long> postTagsSubquery = query.subquery(Long.class); Root<PostTags> postTagsRoot = postTagsSubquery.from(PostTags.class); Join<PostTags, Tag> tagJoin = postTagsRoot.join("tag"); // 태그 이름으로 필터링 조건을 생성합니다. Predicate tagNamePredicate = criteriaBuilder.equal(tagJoin.get("tagName"), searchTag); // 서브쿼리의 select 절을 설정합니다. PostTags에서 Post의 ID를 선택합니다. postTagsSubquery.select(postTagsRoot.get("post").get("id")) .where(tagNamePredicate); // 메인 쿼리에서 Post의 ID가 서브쿼리의 결과에 포함되는 조건을 설정합니다. return criteriaBuilder.in(root.get("id")).value(postTagsSubquery); }; }
- 사용 1: title에 search contents를 포함하는 값을 모두 들고오기
- 각 메소드를 따로 작성후 service에 이식 해주었다. 동적쿼리 작성 완료!!
public Page<PostResponseForSearchDto> getPostBySearch(PostRequestDtoForSearch requestDto, Pageable pageable) { Specification<Post> spec = Specification.where(PostSpecifications.distinct()); if (requestDto.getSearchContent() != null && !requestDto.getSearchContent().isEmpty()) { spec = spec.and(PostSpecifications.titleContains(requestDto.getSearchContent())); } if (requestDto.getSearchTags() != null && !requestDto.getSearchTags().isEmpty()) { spec = spec.and(PostSpecifications.tagNameContains(requestDto.getSearchTags())); } Page<Post> postPage = postRepository.findAll(spec, pageable); return postPage.map(this::convertToDto); }
- builder 패턴을 이용한 responseDto 내용 작성
private PostResponseForSearchDto convertToDto(Post post) { MemberProfile memberProfile = memberProfileRepository.findByMemberId(post.getMember().getId(), post.getMember().isDeletedYn()).orElseThrow(); PostDetail postDetail = postDetailRepository.findByPostId(post.getId()).orElseThrow(); PostResponseForSearchDto response = PostResponseForSearchDto.builder() .postId(post.getId()) .title(post.getTitle()) .memberProfileId(memberProfile.getId()) .memberNickname(memberProfile.getNickName()) .memberProfileImage(memberProfile.getProfileImage()) .view(post.getView()) .likeCount(post.getLikeCount()) .createdAt(post.getCreatedAt()) .updatedAt(post.getUpdatedAt()) .content(postDetail.getContent()) .build(); return response; }
이미 작성 되어있는 entity는 손을 대지 못하고 특정 entity는 이미 join 되어있는 상태에서 사용하려고 했다.
하지만 post id와 tag id 로 join 되어있는 주체가 post tag였는데
이미 다른 메소드들이 post 로 작성 되어있는 상황이었다.
해당 메소드의 주체를 post로 시작하여 return 값도 post가 되어야 했기에 메소드 설계 자체가 까다로웠다.