Skip to content

Commit

Permalink
Merge pull request #111 from Kid-Bean/refactor/lhj/optimization
Browse files Browse the repository at this point in the history
refactor: WordQuiz 쿼리 최적화
  • Loading branch information
Amepistheo authored Feb 8, 2025
2 parents d821650 + af9991f commit 96af6e4
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import soongsil.kidbean.server.global.util.RandNumUtil;
import soongsil.kidbean.server.member.domain.Member;
import soongsil.kidbean.server.member.exception.MemberNotFoundException;
import soongsil.kidbean.server.member.repository.MemberRepository;
Expand All @@ -21,6 +21,7 @@
import soongsil.kidbean.server.wordquiz.repository.WordRepository;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import static soongsil.kidbean.server.member.exception.errorcode.MemberErrorCode.MEMBER_NOT_FOUND;
import static soongsil.kidbean.server.quizsolve.application.vo.QuizType.WORD_QUIZ;
Expand All @@ -44,15 +45,13 @@ public class WordQuizService {
* @return 랜덤 문제가 들어 있는 DTO
*/
public WordQuizSolveListResponse selectRandomWordQuiz(Long memberId, Integer quizNum) {
long count = wordQuizRepository.countByMemberId(memberId);
int randomOffset = ThreadLocalRandom.current().nextInt((int) (count / quizNum));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(MEMBER_NOT_FOUND));

int totalQuizNum = getWordQuizCount(member);
List<Long> randomQuizIds = wordQuizRepository.findRandomQuizIds(memberId, PageRequest.of(randomOffset, quizNum));

List<WordQuizSolveResponse> wordQuizSolveResponseList =
RandNumUtil.generateRandomNumbers(0, totalQuizNum - 1, quizNum).stream()
.map(quizIdx -> generateRandomWordQuizPage(member, quizIdx))
wordQuizRepository.findByIdsWithWords(randomQuizIds).stream()
.map(WordQuizSolveResponse::from)
.toList();

Expand All @@ -64,7 +63,7 @@ private int getWordQuizCount(Member member) {
}

private WordQuiz generateRandomWordQuizPage(Member member, int quizIdx) {
return wordQuizRepository.findSingleResultByMember(member, (long) quizIdx).get(0);
return wordQuizRepository.findSingleResultByMember(member, PageRequest.of(quizIdx, 1)).get(0);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import soongsil.kidbean.server.quizsolve.domain.type.QuizCategory;

@Table(name = "word_quiz")
Expand All @@ -36,6 +38,12 @@ public class WordQuiz {
@Enumerated(EnumType.STRING)
private Level level;

@Column(name = "is_default")
private Boolean isDefault;

@Column(name = "rand_val")
private Long randVal;

@JoinColumn(name = "member_id")
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
Expand All @@ -44,14 +52,16 @@ public class WordQuiz {
private List<Word> words = new ArrayList<>();

@Builder
public WordQuiz(QuizCategory quizCategory, String title, String answer, Level level, Member member,
List<Word> words) {
public WordQuiz(QuizCategory quizCategory, String title, String answer, Level level,
Boolean isDefault, Member member, List<Word> words) {
this.quizCategory = quizCategory;
this.title = title;
this.answer = answer;
this.level = level;
this.isDefault = isDefault;
this.member = member;
this.words = words;
this.randVal = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
}

public void updateWordQuiz(String title, String answer) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package soongsil.kidbean.server.wordquiz.dto.response;

import lombok.Builder;

import java.util.List;

@Builder
public record WordQuizSolveListResponse(
List<WordQuizSolveResponse> wordQuizSolveResponseList
) {
}
public static WordQuizSolveListResponse from(List<WordQuizSolveResponse> wordQuizSolveResponseList) {
return WordQuizSolveListResponse.builder()
.wordQuizSolveResponseList(wordQuizSolveResponseList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package soongsil.kidbean.server.wordquiz.repository;

import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
Expand All @@ -15,8 +16,17 @@ public interface WordQuizRepository extends JpaRepository<WordQuiz, Long> {
@Query("SELECT count(*) FROM WordQuiz wq WHERE wq.member = :member OR wq.member.role = 'ADMIN'")
Integer countByMemberOrAdmin(Member member);

@Query("SELECT wq FROM WordQuiz wq JOIN FETCH wq.words WHERE wq.quizId = :quizId AND wq.member = :member OR wq.member.role = 'ADMIN'")
List<WordQuiz> findSingleResultByMember(Member member, Long quizId);
@Query("SELECT wq FROM WordQuiz wq JOIN FETCH wq.words WHERE wq.member = :member OR wq.member.role = 'ADMIN'")
List<WordQuiz> findSingleResultByMember(Member member, Pageable pageable);

@Query("SELECT COUNT(wq) FROM WordQuiz wq WHERE wq.member.memberId = :memberId OR wq.isDefault = TRUE")
long countByMemberId(Long memberId);

@Query("SELECT wq.quizId FROM WordQuiz wq WHERE wq.member.memberId = :memberId OR wq.isDefault = TRUE ORDER BY wq.randVal")
List<Long> findRandomQuizIds(Long memberId, Pageable pageable);

@Query("SELECT wq FROM WordQuiz wq JOIN FETCH wq.words w WHERE wq.quizId IN :quizIds ORDER BY wq.randVal")
List<WordQuiz> findByIdsWithWords(List<Long> quizIds);

Optional<WordQuiz> findByQuizIdAndMember_MemberId(Long quizId, Long memberId);

Expand Down
Loading

0 comments on commit 96af6e4

Please sign in to comment.