1
1
package com .wsws .moduleinfra .repo .feed ;
2
2
3
+ import com .querydsl .core .BooleanBuilder ;
4
+ import com .querydsl .core .types .dsl .BooleanExpression ;
5
+ import com .querydsl .jpa .impl .JPAQueryFactory ;
3
6
import com .wsws .moduledomain .feed .answer .Answer ;
4
7
import com .wsws .moduledomain .feed .answer .repo .AnswerRepository ;
5
8
import com .wsws .moduledomain .feed .dto .AnswerQuestionDTO ;
9
+ import com .wsws .moduledomain .feed .question .vo .QuestionStatus ;
6
10
import com .wsws .moduleinfra .entity .feed .AnswerEntity ;
7
11
import com .wsws .moduleinfra .entity .feed .QuestionEntity ;
8
12
import com .wsws .moduleinfra .entity .feed .mapper .AnswerEntityMapper ;
11
15
import org .springframework .data .domain .Pageable ;
12
16
import org .springframework .stereotype .Repository ;
13
17
import org .springframework .transaction .annotation .Transactional ;
18
+ import org .springframework .util .StringUtils ;
14
19
15
20
import java .time .LocalDateTime ;
16
21
import java .util .List ;
17
22
import java .util .Optional ;
23
+ import java .util .function .Supplier ;
24
+
25
+ import static com .wsws .moduledomain .feed .question .vo .QuestionStatus .ACTIVATED ;
26
+ import static com .wsws .moduleinfra .entity .feed .QAnswerEntity .answerEntity ;
27
+ import static com .wsws .moduleinfra .entity .feed .QQuestionEntity .questionEntity ;
28
+ import static org .springframework .util .StringUtils .hasText ;
18
29
19
30
@ Repository
20
31
@ RequiredArgsConstructor
21
32
public class AnswerRepositoryImpl implements AnswerRepository {
22
33
23
34
private final JpaAnswerRepository jpaAnswerRepository ;
24
35
private final JpaQuestionRepository jpaQuestionRepository ;
36
+ private final JPAQueryFactory queryFactory ;
25
37
26
38
/**
27
39
* 답변을 Id를 기준으로 찾기
@@ -41,35 +53,54 @@ public Optional<Answer> findByIdWithLock(Long id) {
41
53
42
54
@ Override
43
55
public List <Answer > findAllByCategoryIdWithCursor (LocalDateTime cursor , int size , Long categoryId ) {
44
- Pageable pageable = PageRequest .of (0 , size ); // 가져올 데이터 갯수 설정
45
- return categoryId == null
46
- ?
47
- jpaAnswerRepository .findAllWithCursor (cursor , pageable ).stream () // categoryId가 없다면 전체 조회
48
- .map (AnswerEntityMapper ::toDomain )
49
- .toList ()
50
- :jpaAnswerRepository .findAllByCategoryIdWithCursor (cursor , pageable , categoryId ).stream () // categoryId가 있다면 해당 카테고리로 조회
56
+ List <AnswerEntity > answerEntities = queryFactory
57
+ .select (answerEntity )
58
+ .from (answerEntity )
59
+ .join (answerEntity .questionEntity , questionEntity )
60
+ .where (
61
+ categoryIdEq (categoryId ),
62
+ questionEntity .questionStatus .eq (ACTIVATED ),
63
+ answerEntity .createdAt .lt (cursor )
64
+ ).orderBy (answerEntity .createdAt .desc ())
65
+ .offset (0 )
66
+ .limit (size )
67
+ .fetch ();
68
+
69
+ return answerEntities .stream ()
51
70
.map (AnswerEntityMapper ::toDomain )
52
71
.toList ();
53
72
}
54
73
55
74
@ Override
56
75
public List <AnswerQuestionDTO > findAllByUserIdWithCursor (
57
76
String userId , LocalDateTime cursor , int size , boolean isMine ) {
58
- Pageable pageable = PageRequest .of (0 , size ); // 가져올 데이터 갯수 설정
59
- List <AnswerEntity > answerEntities = isMine
60
- ? jpaAnswerRepository .findAllByUserIdWithCursor (userId , cursor , pageable )
61
- : jpaAnswerRepository .findAllByUserIdAndVisibilityTrueWithCursor (userId , cursor , pageable );
77
+
78
+ List <AnswerEntity > answerEntities = queryFactory
79
+ .selectFrom (answerEntity )
80
+ .join (answerEntity .questionEntity , questionEntity ).fetchJoin ()
81
+ .where (
82
+ visibilityEqTrue (isMine ),
83
+ answerEntity .userId .eq (userId ),
84
+ answerEntity .createdAt .lt (cursor )
85
+ ).orderBy (answerEntity .createdAt .desc ())
86
+ .offset (0 )
87
+ .limit (size )
88
+ .fetch ();
62
89
63
90
return answerEntities .stream ()
64
91
.map (AnswerEntityMapper ::toJoinDto )
65
92
.toList ();
66
93
}
67
94
68
-
69
95
@ Override
70
96
public Long countByUserId (String userId , boolean isMine ) {
71
- return isMine ? jpaAnswerRepository .countByUserId (userId ) // 요청한 사용자의 질문이면 모든 Answer
72
- : jpaAnswerRepository .countByUserIdAndVisibilityTrue (userId ); // 요청한 사용자의 질문이 아니면 visibility가 true인 Answer만
97
+ return queryFactory
98
+ .select (answerEntity .count ())
99
+ .from (answerEntity )
100
+ .where (
101
+ visibilityEqTrue (isMine ),
102
+ answerEntity .userId .eq (userId )
103
+ ).fetchFirst ();
73
104
}
74
105
75
106
@ Override
@@ -78,18 +109,25 @@ public Optional<Answer> findAnswerByUserIdAndQuestionId(String userId, Long ques
78
109
.map (AnswerEntityMapper ::toDomain );
79
110
}
80
111
112
+ // TODO: 동적 쿼리 Querydsl로 수정
81
113
@ Override
82
114
public List <Answer > findAnswersByLikeCountAndCategoryIdWithCursor (Long categoryId , int limit ) {
83
- Pageable pageable = PageRequest .of (0 , limit ); // 가져올 데이터 갯수 설정
84
- return categoryId == null
85
- ? jpaAnswerRepository .findAllOrderByLikeCountDescWithCursor (pageable ).stream () // categoryId가 없다면 전체 조회
86
- .map (AnswerEntityMapper ::toDomain )
87
- .toList ()
88
- : jpaAnswerRepository .findAllByCategoryIdOrderByLikeCountDescWithCursor (categoryId , pageable ).stream () // categoryId가 있다면 해당 categoryId로 조회
115
+
116
+ List <AnswerEntity > answerEntities = queryFactory
117
+ .selectFrom (answerEntity )
118
+ .join (answerEntity .questionEntity , questionEntity )
119
+ .where (
120
+ categoryIdEq (categoryId ),
121
+ questionEntity .questionStatus .eq (ACTIVATED )
122
+ ).orderBy (answerEntity .likeCount .desc ())
123
+ .offset (0 )
124
+ .limit (limit )
125
+ .fetch ();
126
+
127
+ return answerEntities .stream ()
89
128
.map (AnswerEntityMapper ::toDomain )
90
129
.toList ();
91
130
}
92
-
93
131
/**
94
132
* 답변 저장
95
133
*/
@@ -98,8 +136,8 @@ public List<Answer> findAnswersByLikeCountAndCategoryIdWithCursor(Long categoryI
98
136
public Answer save (Answer answer ) {
99
137
AnswerEntity answerEntity = AnswerEntityMapper .toEntity (answer );
100
138
101
- QuestionEntity questionEntity = jpaQuestionRepository .findById (answer .getQuestionId ().getValue ()). orElse ( null );
102
- answerEntity . setQuestionEntity ( questionEntity ); // Quesiton 연관관계 설정
139
+ jpaQuestionRepository .findById (answer .getQuestionId ().getValue ())
140
+ . ifPresent ( answerEntity :: setQuestionEntity ); // Quesiton 연관관계 설정
103
141
104
142
AnswerEntity savedEntity = jpaAnswerRepository .save (answerEntity );// Answer를 엔티티로 변환하여 저장
105
143
return AnswerEntityMapper .toDomain (savedEntity );
@@ -129,4 +167,23 @@ public void edit(Answer answer) {
129
167
public void deleteById (Long id ) {
130
168
jpaAnswerRepository .deleteById (id );
131
169
}
170
+
171
+ private BooleanBuilder categoryIdEq (Long categoryId ) {
172
+ return nullSafeBuilder (() -> questionEntity .categoryId .eq (categoryId ), categoryId );
173
+ }
174
+
175
+ private BooleanBuilder visibilityEqTrue (boolean isMine ) {
176
+ return nullSafeBuilder (() -> answerEntity .visibility .eq (true ), null );
177
+ }
178
+
179
+ private <T > BooleanBuilder nullSafeBuilder (Supplier <BooleanExpression > f , T value ) {
180
+ if (value instanceof String && !hasText ((String ) value )) {
181
+ return new BooleanBuilder ();
182
+ }
183
+ try {
184
+ return new BooleanBuilder (f .get ());
185
+ } catch (Exception e ) {
186
+ return new BooleanBuilder ();
187
+ }
188
+ }
132
189
}
0 commit comments