Skip to content

Commit

Permalink
Merge branch 'refacotr/#148/mypage-optimization' of https://github.co…
Browse files Browse the repository at this point in the history
…m/dnd-side-project/dnd-11th-3-backend into refacotr/#148/mypage-optimization
  • Loading branch information
hyun2371 committed Nov 23, 2024
2 parents 26bfa04 + 49f801e commit 4c52777
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public ResponseEntity<PageResponse<AnswerDetailResponse>> getAnswersByQuestionPo
@ApiResponse(useReturnTypeSchema = true)
@PostMapping("/api/question-posts/answers/{answerId}")
public ResponseEntity<AnswerDetailResponse> getAnswersByQuestionPostId(
@PathVariable Long answerId,
@PathVariable("answerId") Long answerId,
@AuthenticationPrincipal Member member
) {
AnswerDetailResponse response = answerService.chooseAnswer(answerId, member);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.dnd.gongmuin.answer.repository;

import java.util.Optional;

import org.springframework.stereotype.Repository;

import com.dnd.gongmuin.answer.domain.Answer;

import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class AnswerSimpleQueryRepository {

private final EntityManager em;

public Optional<Answer> findAnswerById(Long answerId) {
Answer answer = em.createQuery(
"select a from Answer a" +
" join fetch a.member m" +
" where a.id = :answerId", Answer.class
)
.setParameter("answerId", answerId)
.getSingleResult();
return Optional.of(answer);
}
}
15 changes: 12 additions & 3 deletions src/main/java/com/dnd/gongmuin/answer/service/AnswerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.dnd.gongmuin.answer.dto.RegisterAnswerRequest;
import com.dnd.gongmuin.answer.exception.AnswerErrorCode;
import com.dnd.gongmuin.answer.repository.AnswerRepository;
import com.dnd.gongmuin.answer.repository.AnswerSimpleQueryRepository;
import com.dnd.gongmuin.common.dto.PageMapper;
import com.dnd.gongmuin.common.dto.PageResponse;
import com.dnd.gongmuin.common.exception.runtime.NotFoundException;
Expand All @@ -23,6 +24,7 @@
import com.dnd.gongmuin.question_post.domain.QuestionPost;
import com.dnd.gongmuin.question_post.exception.QuestionPostErrorCode;
import com.dnd.gongmuin.question_post.repository.QuestionPostRepository;
import com.dnd.gongmuin.question_post.repository.QuestionPostSimpleQueryRepository;

import lombok.RequiredArgsConstructor;

Expand All @@ -34,6 +36,8 @@ public class AnswerService {
private final AnswerRepository answerRepository;
private final CreditHistoryService creditHistoryService;
private final ApplicationEventPublisher eventPublisher;
private final QuestionPostSimpleQueryRepository questionPostSimpleQueryRepository;
private final AnswerSimpleQueryRepository answerSimpleQueryRepository;

private static void validateIfQuestioner(Member member, QuestionPost questionPost) {
if (!questionPost.isQuestioner(member.getId())) {
Expand All @@ -47,7 +51,7 @@ public AnswerDetailResponse registerAnswer(
RegisterAnswerRequest request,
Member member
) {
QuestionPost questionPost = findQuestionPostById(questionPostId);
QuestionPost questionPost = getQuestionPostById(questionPostId);
Answer answer = AnswerMapper.toAnswer(questionPostId, questionPost.isQuestioner(member.getId()), request,
member);
Answer savedAnswer = answerRepository.save(answer);
Expand Down Expand Up @@ -100,12 +104,17 @@ private void validateIfQuestionPostExists(Long questionPostId) {
}

private Answer getAnswerById(Long answerId) {
return answerRepository.findById(answerId)
return answerSimpleQueryRepository.findAnswerById(answerId)
.orElseThrow(() -> new NotFoundException(AnswerErrorCode.NOT_FOUND_ANSWER));
}

private QuestionPost findQuestionPostById(Long questionPostId) {
private QuestionPost getQuestionPostById(Long questionPostId) {
return questionPostRepository.findById(questionPostId)
.orElseThrow(() -> new NotFoundException(QuestionPostErrorCode.NOT_FOUND_QUESTION_POST));
}

private QuestionPost findQuestionPostById(Long questionPostId) {
return questionPostSimpleQueryRepository.findQuestionPostById(questionPostId)
.orElseThrow(() -> new NotFoundException(QuestionPostErrorCode.NOT_FOUND_QUESTION_POST));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.dnd.gongmuin.question_post.repository;

import java.util.Optional;

import org.springframework.stereotype.Repository;

import com.dnd.gongmuin.question_post.domain.QuestionPost;

import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class QuestionPostSimpleQueryRepository {

private final EntityManager em;

public Optional<QuestionPost> findQuestionPostById(Long questionPostId) {
QuestionPost questionPost = em.createQuery(
"select q from QuestionPost q" +
" join fetch q.member m" +
" where q.id = :questionPostId", QuestionPost.class
)
.setParameter("questionPostId", questionPostId)
.getSingleResult();
return Optional.of(questionPost);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ void chooseAnswer() throws Exception {
= questionPostRepository.save(QuestionPostFixture.questionPost(loginMember));
Member answerer = memberRepository.save(MemberFixture.member2());
Answer answer = answerRepository.save(AnswerFixture.answer(questionPost.getId(), answerer));
long startTime = System.currentTimeMillis();

mockMvc.perform(post("/api/question-posts/answers/{answerId}", answer.getId())
.cookie(accessToken)
Expand All @@ -137,5 +138,7 @@ void chooseAnswer() throws Exception {
.andExpect(jsonPath("$.memberInfo.nickname").value(answerer.getNickname()))
.andExpect(jsonPath("$.memberInfo.memberJobGroup").value(answerer.getJobGroup().getLabel())
);
long endTime = System.currentTimeMillis();
System.out.println("Execution time: " + (endTime - startTime) + " ms");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -24,6 +28,7 @@
import com.dnd.gongmuin.answer.dto.AnswerDetailResponse;
import com.dnd.gongmuin.answer.dto.RegisterAnswerRequest;
import com.dnd.gongmuin.answer.repository.AnswerRepository;
import com.dnd.gongmuin.answer.repository.AnswerSimpleQueryRepository;
import com.dnd.gongmuin.common.dto.PageResponse;
import com.dnd.gongmuin.common.exception.runtime.ValidationException;
import com.dnd.gongmuin.common.fixture.AnswerFixture;
Expand All @@ -36,6 +41,7 @@
import com.dnd.gongmuin.question_post.domain.QuestionPost;
import com.dnd.gongmuin.question_post.exception.QuestionPostErrorCode;
import com.dnd.gongmuin.question_post.repository.QuestionPostRepository;
import com.dnd.gongmuin.question_post.repository.QuestionPostSimpleQueryRepository;

@DisplayName("[AnswerService 테스트]")
@ExtendWith(MockitoExtension.class)
Expand All @@ -46,9 +52,15 @@ class AnswerServiceTest {
@Mock
private QuestionPostRepository questionPostRepository;

@Mock
private QuestionPostSimpleQueryRepository questionPostSimpleQueryRepository;

@Mock
private AnswerRepository answerRepository;

@Mock
private AnswerSimpleQueryRepository answerSimpleQueryRepository;

@Mock
private CreditHistoryService creditHistoryService;

Expand Down Expand Up @@ -118,9 +130,9 @@ void chooseAnswer() {
QuestionPost questionPost = QuestionPostFixture.questionPost(questionPostId, member);
Answer answer = AnswerFixture.answer(1L, questionPostId);

given(answerRepository.findById(answer.getId()))
given(answerSimpleQueryRepository.findAnswerById(answer.getId()))
.willReturn(Optional.of(answer));
given(questionPostRepository.findById(questionPost.getId()))
given(questionPostSimpleQueryRepository.findQuestionPostById(questionPost.getId()))
.willReturn(Optional.of(questionPost));

//when
Expand All @@ -140,9 +152,9 @@ void chooseAnswer_fail() {
ReflectionTestUtils.setField(questionPost, "reward", member.getCredit() + 1);
Answer answer = AnswerFixture.answer(1L, questionPostId);

given(answerRepository.findById(answer.getId()))
given(answerSimpleQueryRepository.findAnswerById(answer.getId()))
.willReturn(Optional.of(answer));
given(questionPostRepository.findById(questionPost.getId()))
given(questionPostSimpleQueryRepository.findQuestionPostById(questionPost.getId()))
.willReturn(Optional.of(questionPost));

//when & then
Expand All @@ -162,14 +174,65 @@ void chooseAnswer_fail2() {
QuestionPost questionPost = QuestionPostFixture.questionPost(questionPostId, questioner);
Answer answer = AnswerFixture.answer(1L, questionPostId);

given(answerRepository.findById(answer.getId()))
given(answerSimpleQueryRepository.findAnswerById(answer.getId()))
.willReturn(Optional.of(answer));
given(questionPostRepository.findById(questionPost.getId()))
given(questionPostSimpleQueryRepository.findQuestionPostById(questionPost.getId()))
.willReturn(Optional.of(questionPost));

//when & then
assertThatThrownBy(() -> answerService.chooseAnswer(answer.getId(), notQuestioner))
.isInstanceOf(ValidationException.class)
.hasMessageContaining(QuestionPostErrorCode.NOT_AUTHORIZED.getMessage());
}

@DisplayName("[동시에 10_000개의 채택이 일어나 크레딧을 입금 받는다.]")
@Test
void creditHistoryWithOneHundred() throws Exception {
// given
final long threadCount = 10_000L;
final int writerCredit = 10_000_000;
ExecutorService executorService = Executors.newFixedThreadPool(32);
CountDownLatch latch = new CountDownLatch((int)threadCount);

Member writer = MemberFixture.member(1L);
Member answer = MemberFixture.member(2L);
ReflectionTestUtils.setField(writer, "credit", writerCredit);
ReflectionTestUtils.setField(answer, "credit", 0);

List<QuestionPost> questionPosts = new ArrayList<>();
List<Answer> answers = new ArrayList<>();

for (long i = 1L; i <= threadCount; i++) {
QuestionPost questionPost = QuestionPostFixture.questionPost(i, writer);

Answer answer1 = AnswerFixture.answer(questionPost.getId(), answer);
ReflectionTestUtils.setField(answer1, "id", i);
questionPosts.add(questionPost);
answers.add(answer1);

given(answerSimpleQueryRepository.findAnswerById(i)).willReturn(Optional.of(answer1));
given(questionPostSimpleQueryRepository.findQuestionPostById(questionPost.getId()))
.willReturn(Optional.of(questionPost));
}

// when
long startTime = System.currentTimeMillis();
for (long i = 0L; i < threadCount; i++) {
final int index = (int)i;
executorService.submit(() -> {
try {
answerService.chooseAnswer(answers.get(index).getId(), writer);
} finally {
latch.countDown();
}
});
}
latch.await();

long endTime = System.currentTimeMillis();
System.out.println("Execution time: " + (endTime - startTime) + " ms");

// then
assertEquals(answer.getCredit(), writerCredit);
}
}

0 comments on commit 4c52777

Please sign in to comment.