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내맛대로 바꿨지만 코드 몇줄 축약 된 것 뿐, 건든 만큼 체감이 되지 않아 더 건들여봐야 할 것 같다.