QueryDsl BooleanExpression Paging, OrderBy 적용

XingXi·2024년 1월 22일
0

기록

목록 보기
9/33

🍕 목표

JPA 와 Querydsl 를 활용하는 연습을 진행하고 있다.
오늘의 간단한 목표는 OrderBy 정렬을 사용하는 것이다.


🍕 사용할 엔티티들

POST

@Entity
@Getter
@Builder
public class Post extends BaseEntity
{
    @Id
    @GeneratedValue
    @Column(name = "post_pid")
    private Long pid;

    private String title;

    @Builder.Default
    private Integer view = 0;

    @Column(columnDefinition="TEXT")
    private String contents;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "auth_pid")
    private Auth auth;
}

Auth

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"loginId"}))
public class Auth extends BaseEntity
{

    @Id @GeneratedValue
    @Column(name = "auth_pid")
    private Long pid;

    private String loginId;
    private String password;

    @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.DETACH)
    @JoinColumn(name = "refresh_token_pid")
    private RefreshToken refreshToken;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_pid")
    private Member member;

    public void linkMember(Member member)
    {
        this.member = member;
        member.getAuths().add(this);
    }

    @OneToMany(mappedBy = "auth")
    @Builder.Default
    private List<Post> posts = new ArrayList<>();

    @OneToMany(mappedBy = "auth")
    @Builder.Default
    private List<RoleAndAuth> roleAndAuths = new ArrayList<>();
}

Member

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(uniqueConstraints = @UniqueConstraint( columnNames = {"userEmail","userNick"}))
public class Member extends BaseEntity
{

    @Id @GeneratedValue
    @Column(name = "member_pid")
    private Long pid;

    private String userName;

    private int age;

    private String userEmail;

    private String phone;

    private String userNick;

    @Embedded
    private Address address;

    @OneToMany(mappedBy = "member")
    @Builder.Default
    private List<Auth> auths = new ArrayList<>();

}

🍕 사용할 엔티티의 연관관계

--

🍕 사용한 DTO

1. SearchPostDto

@Data
public class SearchPostDto {
    private String authorNick   ;
    private String title        ;
    private String contents     ;
    private boolean isAsc       ;
}

POST에 대한 정보를 조회 할 때 사용한다.

2. SearchPostResultDto

@Data
@ToString
public class SearchPostResultDto {
    private String authorNick   ;
    private String title        ;
    private String contents     ;
    private Integer views       ;
}

POST 정보 요청에 대한 반환 값 DTO


🍕 레파지토리

public class PostRepositoryImpl implements PostRepositoryCustom{

    private final JPAQueryFactory queryFactory;

    public PostRepositoryImpl(EntityManager em)
    {
        queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public Page<SearchPostResultDto> getSearchPost(SearchPostDto request, Pageable pageable) {
        List<SearchPostResultDto> searchPostReq =

                queryFactory.select(
                        Projections.fields(
                                SearchPostResultDto.class,
                                member.userNick.as("authorNick"),
                                post.title.as("title"),
                                post.contents.as("contents"),
                                post.view.as("views"))
                        )
                        .from(post)
                        .join(post.auth , auth)
                        .join(auth.member, member)
                        .where(
                            authorNickEq(request.getAuthorNick()),
                                postTitleEq(request.getTitle()),
                                postContentsEq(request.getContents())
                        )
                        .orderBy(request.isAsc() ? post.view.asc() : post.view.desc())
                        .offset(pageable.getOffset())
                        .limit(pageable.getPageSize())
                        .fetch();

        System.out.println("ABLE");

        return new PageImpl<>(searchPostReq,pageable,searchPostReq.size());
    }

    private BooleanExpression postTitleEq(String title){
        return hasText(title) ? post.title.like("%" + title + "%") : null;
    }

    private BooleanExpression postContentsEq(String contents){
        return hasText(contents) ? post.contents.like("%" + contents + "%") : null;
    }

    private BooleanExpression authorNickEq(String authorNick){
        return hasText(authorNick) ? member.userNick.like("%" + authorNick + "%") : null;
    }
}

1. 굳이 EntityManager 를 ...

    private final JPAQueryFactory queryFactory;

    public PostRepositoryImpl(EntityManager em)
    {
        queryFactory = new JPAQueryFactory(em);
    }

추후 Spring DATA JPA 의 Save Method 를 사용하지 않고 엔티티를 저장하는 메소드를 작성하기 위해

2. Projections.fields 를 사용하여 결과 값을 DTO 로 바로 받음

                queryFactory.select(
                        Projections.fields(
                                SearchPostResultDto.class,
                                member.userNick.as("authorNick"),
                                post.title.as("title"),
                                post.contents.as("contents"),
                                post.view.as("views"))

Query 결과 값을 DTO 에 받으려고 한다. 많은 방식 들 중 fields 에 직접 받는 방식을 사용하였다. 필드이름이 다르기 때문에 as 메소드를 활용하여 매핑하였다.

3. 3개의 BooleanExpression

    private BooleanExpression postTitleEq(String title){
        return hasText(title) ? post.title.like("%" + title + "%") : null;
    }

    private BooleanExpression postContentsEq(String contents){
        return hasText(contents) ? post.contents.like("%" + contents + "%") : null;
    }

    private BooleanExpression authorNickEq(String authorNick){
        return hasText(authorNick) ? member.userNick.like("%" + authorNick + "%") : null;
    }

다이나믹 서치 기능을 생각해보면 글자를 포함하는 경우까지 들고오는 것을 생각 할 수 있다.
검색 조건은 게시글 ( POST )제목 ( title ) , 내용 ( contents ), 작성자 닉네임 ( authorNick )을 사용했다.

4. OrderBy

orderBy(request.isAsc() ? post.view.asc() : post.view.desc())

isAsc 필드 값이 true이면 조회 수 ( view 필드 ) 를 기준으로 오름차순 아닐 경우 내림차순이 되도록 설정 했다.


결과

1.테스트 코드

    @Autowired
    PostRepository postRepository;

    @Autowired
    CreateUpdateMemberService createUpdateMemberService;

    @Autowired
    AuthRepository authRepository;

    @BeforeEach
    void beforeSetting()
    {
        MemberInfoDto memberInfoDto = MemberInfoDto.builder()
                .age(10)
                .city("경기도 성남시")
                .phone("010-1234-5678")
                .loginId("testLoginId01")
                .password("Qwer!234")
                .street("대왕판교로")
                .userEmail("testLoginId01@naver.com")
                .userName("테스트01")
                .userNick("TEST01")
                .zipcode("12345")
                .build();

        long test01Pid = createUpdateMemberService.createMember(memberInfoDto);


        MemberInfoDto memberInfoDto2 = MemberInfoDto.builder()
                .age(10)
                .city("경기도 성남시")
                .phone("010-1234-5679")
                .loginId("testLoginId02")
                .password("Qwer!234")
                .street("대왕판교로")
                .userEmail("testLoginId02@naver.com")
                .userName("테스트02")
                .userNick("TEST02")
                .zipcode("12345")
                .build();

        long test02Pid = createUpdateMemberService.createMember(memberInfoDto2);

        Auth auth01 = authRepository.findById(test01Pid).orElseThrow(NotFoundLoginId::new);
        Auth auth02 = authRepository.findById(test02Pid).orElseThrow(NotFoundLoginId::new);

        Post post1 = Post.builder()
                .title("abcde")
                .contents("가나다라마")
                .view(0)
                .auth(auth01).build();

        Post post2 = Post.builder()
                .title("stuvw")
                .contents("바사아자")
                .view(100)
                .auth(auth01).build();

        Post post3 = Post.builder()
                .title("kkkkkk")
                .contents("가나다라마")
                .view(20)
                .auth(auth02).build();

        Post post4 = Post.builder()
                .title("kkka")
                .contents("바사아자")
                .view(1200)
                .auth(auth02).build();

        List<Post> postList = new ArrayList<>();
        postList.add(post1);
        postList.add(post2);
        postList.add(post3);
        postList.add(post4);

        postRepository.saveAll(postList);

    }

    @Test
    void find_Post_BY_Search()
    {
        SearchPostDto request = new SearchPostDto();
        request.setAsc(true);

        Page<SearchPostResultDto> searchPost = postRepository.getSearchPost(request, PageRequest.of(0, 4));

        for (SearchPostResultDto searchPostResultDto : searchPost) {

            System.out.println("searchPostResultDto : "+searchPostResultDto.toString() );
        }
    }

조회 수 기준으로 오름차순 정렬하는 결과 값을 요청

2. 테스트 코드 결과

ABLE
searchPostResultDto : SearchPostResultDto(authorNick=TEST01, title=abcde, contents=가나다라마, views=0)
searchPostResultDto : SearchPostResultDto(authorNick=TEST02, title=kkkkkk, contents=가나다라마, views=20)
searchPostResultDto : SearchPostResultDto(authorNick=TEST01, title=stuvw, contents=바사아자, views=100)
searchPostResultDto : SearchPostResultDto(authorNick=TEST02, title=kkka, contents=바사아자, views=1200)

조회 수 기준으로 오름차순 정렬을 return 받은 것을 확인할 수 있다.

0개의 댓글