Post

Querydsl 1.5편 - QuerydslRepositorySupport 내맛대로 만들기

Querydsl 1.5편 - QuerydslRepositorySupport 내맛대로 만들기

QuerydslRepositorySupport


앞서 이 글은 오늘의 주제에 대해서 아직 완전히 습득을 하지 못하여, 여태까지 이해한 과정을 복기하고자 쓴다.
1편에서 QueryDsl를 배우면서 QuerydslRepositorySupport를 기능을 용이하게 쓰면서 이론까지 배웠는데, 거기에 더하여 이 기능의 한계도 있었기에 내 마음대로 수정을 해서 만들어보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@Repository
public abstract class CustomQuerydslRepositorySupport {

    private final PathBuilder<?> builder;
    private @Nullable EntityManager entityManager;
    private @Nullable Querydsl querydsl;  
    private JPAQueryFactory queryFactory; //추가한 부분

    public Querydsl4RepositorySupport(Class<?> domainClass) {

        Assert.notNull(domainClass, "Domain class must not be null");
        this.builder = new PathBuilderFactory().create(domainClass);
    }


    @Autowired
    public void setEntityManager(EntityManager entityManager) {

        Assert.notNull(entityManager, "EntityManager must not be null");
        this.querydsl = new Querydsl(entityManager, builder);
        this.entityManager = entityManager;
        this.queryFactory = new JPAQueryFactory(entityManager);
    }


    @PostConstruct
    public void validate() {
        Assert.notNull(entityManager, "EntityManager must not be null");
        Assert.notNull(querydsl, "Querydsl must not be null");
        Assert.notNull(queryFactory, "QueryFactory must not be null!");
    }

    @Nullable
    protected EntityManager getEntityManager() {
        return entityManager;
    }

    protected JPQLQuery<Object> from(EntityPath<?>... paths) {
        return getRequiredQuerydsl().createQuery(paths);
    }

    protected <T> JPQLQuery<T> from(EntityPath<T> path) {
        return getRequiredQuerydsl().createQuery(path).select(path);
    }

    protected DeleteClause<JPADeleteClause> delete(EntityPath<?> path) {
        return new JPADeleteClause(getRequiredEntityManager(), path);
    }

    protected UpdateClause<JPAUpdateClause> update(EntityPath<?> path) {
        return new JPAUpdateClause(getRequiredEntityManager(), path);
    }

    @SuppressWarnings("unchecked")
    protected <T> PathBuilder<T> getBuilder() {
        return (PathBuilder<T>) builder;
    }


    @Nullable
    protected Querydsl getQuerydsl() {
        return this.querydsl;
    }

    private Querydsl getRequiredQuerydsl() {

        if (querydsl == null) {
            throw new IllegalStateException("Querydsl is null");
        }

        return querydsl;
    }
    protected JPAQueryFactory getQueryFactory() {//추가한 부분
        return queryFactory;                        
    }
    private EntityManager getRequiredEntityManager() {

        if (entityManager == null) {
            throw new IllegalStateException("EntityManager is null");
        }

        return entityManager;
    }

    protected <T> JPAQuery<T> select(Expression<T> expr) {//추가한 부분
        return getQueryFactory().select(expr);         
    }

    protected <T> JPAQuery<T> selectFrom(EntityPath<T> from) {  //추가한 부분
        return getQueryFactory().selectFrom(from);
    }

    protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery) {////////////////////////////////추가한 부분
        JPAQuery jpaQuery = contentQuery.apply(getQueryFactory());
        List<T> content = getQuerydsl().applyPagination(pageable, jpaQuery).fetch();
        return PageableExecutionUtils.getPage(content, pageable, jpaQuery::fetchCount);
    }

    protected <T> Page<T> applyPagination(Pageable pageable,////////////////////추가한 부분
                                          Function<JPAQueryFactory, JPAQuery> contentQuery, Function<JPAQueryFactory,
            JPAQuery> countQuery) {
        JPAQuery jpaContentQuery = contentQuery.apply(getQueryFactory());
        List<T> content = getQuerydsl().applyPagination(pageable, jpaContentQuery).fetch();
        JPAQuery countResult = countQuery.apply(getQueryFactory());
        return PageableExecutionUtils.getPage(content, pageable, countResult::fetchCount);
    }
}

CustomQuerydslRepositorySupport를 새로 클래스를 만들어 기존QuerydslRepositorySupport에서 JPAQueryFactory를 추가해 기존의 쿼리문을 실행하는 명령문 select시작할 수 있게 설정하였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   public Page<Board> searchAll(String[] types, String keyword, Pageable pageable){

       QBoard board = QBoard.board;
       JPQLQuery<Board> query = from(board);
     
        if((types != null && types.length > 0) && keyword != null){
            BooleanBuilder booleanBuilder = new BooleanBuilder();
            for(String type : types){
                switch (type){
                    case "t":
                        booleanBuilder.or(board.title.contains(keyword));
                        break;
                    case "c":
                        booleanBuilder.or(board.content.contains(keyword));
                        break;
                    case "w":
                        booleanBuilder.or(board.user.contains(keyword));
                        break;
                }
            }
            query.where(booleanBuilder);
        }
       query.where(board.bno.gt(0L));

       this.getQuerydsl().applyPagination(pageable,query);
       List<Board> list = query.fetch();
       Long count = query.fetchCount();

       return new PageImpl<>(list, pageable, count);

    }

위의 코드는 기존 QuerydslRepositorySupport을 사용하였을 때이다.
이때 페이징은 this.getQuerydsl().applyPagination(pageable,query)을 하여 다시 리스트를 List<Board> list = query.fetch(), 카운트를 Long count = query.fetchCount(); 처리하여 Page로 리턴시켰다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Page<Board> searchAll(String[] types, String keyword, Pageable pageable){

        QBoard board = QBoard.board;
        BooleanBuilder booleanBuilder = new BooleanBuilder();

        if((types != null && types.length > 0) && keyword != null){
            for(String type : types){
                switch (type){
                    case "t":
                        booleanBuilder.or(board.title.contains(keyword));
                        break;
                    case "c":
                        booleanBuilder.or(board.content.contains(keyword));
                        break;
                    case "w":
                        booleanBuilder.or(board.user.contains(keyword));
                        break;
                }
            }
        }

        return applyPagination(pageable, query -> query.selectFrom(board)
                               .where(booleanBuilder,board.bno.gt(0L)));
    }

다시 CustomQuerydslRepositorySupport을 적용하였을 때,페이징을 applyPagination()으로 한번에 처리하여 코드가 깔끔하게 줄였다. 이처럼 필요한 기능을 내가 applyPagination()처럼 만들어서 추가하면 된다.


딴길로 새버렸네…

사실 내가 CustomQuerydslRepositorySupport를 적용해서 페이징을 테스트 케이스로 돌렸을 때 로그에 페이지가 이상한 문자조합으로 출력이 되어 기존과 어떤 차이 때문에 정상적으로 출력이 안되는지 시간을 허비하였다.
그런데 정말 허무하게 해결되었는데 Board엔티티에 @ToString를 추가하니 말끔히 페이지가 출력되었다.
중간에 있는 BooleanBuilder이 기능으로 조건을 만드는 코드가 지저분한 것 같아 BooleanExpression를 이용하여 다시 코드를 정리를 할려는 시도도 테스트 출력이 이상이 있는 바람에 발만 담구는 형식만 되어 유의미한 결과를 얻지 못하였다.
그리고 QuerydslRepositorySupport내맛대로 바꿨지만 코드 몇줄 축약 된 것 뿐, 건든 만큼 체감이 되지 않아 더 건들여봐야 할 것 같다.


This post is licensed under CC BY 4.0 by the author.