관련 내용
<Spring Data JPA와 Querydsl 완전히 이해하기>
- SpringDataJPA 이해와 사용법
- Querydsl 이해와 사용법
- Querydsl JOIN((INNER, LEFT, RIGHT, THETA, FETCH) 사용법
Querydsl 의 JOIN (INNER, LEFT, RIGHT, THETA, FETCH) 사용법
개요 목적
이번 시간에는 동적 쿼리 최적화된 Querydsl를 사용해서, 세가지 연관된 테이블 데이터를 가져오는 방법에 대해서 알아보자.
Querydsl를 사용할 상황 알아보기
해당 웹페이지에서 adminTestListCondition{”userId”: , “grammar”: , “situation”: , “check”: } 정보가 넘어오면
백엔드 페이지 에서는 해당 정보에 맞는 데이터를 세 가지 연관된 테이블(admin_sentence, admin_sentence_check, users) 에서 데이터를 가져와 웹페이지에 전송한다.
문법 체크(grammar) 상황 체크(situation) 요청 값이 “NO”일 때 Where 동적 조건 만들기
웹페이지에서 들어온 요청 grammar와 situation의 값이 “NO”일 경우, where절에 조건을 적용하지 않는다.
예를 들어, 반대로, grammar에 “NO”가 아닌 “IF”가 들어오면, where grammar==”IF”라는 조건을 붙이게 된다.
<동적 where 조건 절 만들기(관련된 정보만 실제 코드에서 추출)>
Slf4j
public class AdminSentenceCustomRepositoryImpl implements AdminSentenceCustomRepository{
JPAQueryFactory queryFactory;
public AdminSentenceCustomRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<Tuple> findTestListByCondition(AdminTestListConditionRequest condition) {
List<Tuple> result = queryFactory
.select(adminSentence.id, adminSentence.korean,
adminSentence.english, adminSentence.grammar, adminSentence.situation,
adminTestCheck.testCheck)
.from(adminSentence)
//만든 메소드(BooleanExpresstion) 실제 where절에 입력
.where(grammarEq(condition.getGrammar()), situationEq(condition.getSituation()))
.innerJoin(adminSentence.adminTestChecks, adminTestCheck)
.on(adminTestCheck.testCheck.eq(check),
adminTestCheck.users.id.eq(condition.getUserId()))
.fetch();
return result;
}
//들어온 요청 정보에 따라 동적 BooleanExpression 만들기
private BooleanExpression grammarEq(String stringGrammar) {
//NO가 오면 where 절 적용 안함
Grammar grammar = Grammar.valueOf(stringGrammar);
return stringGrammar.equals("NO") ? null : adminSentence.grammar.eq(grammar);
}
private BooleanExpression situationEq(String stringSituation) {
//NO가 오면 where 절 적용 안함
Situation situation = Situation.valueOf(stringSituation);
return stringSituation.equals("NO") ? null : adminSentence.situation.eq(situation);
}
}
return stringGrammar.equals("NO") ? null : adminSentence.grammar.eq(grammar);
입력을 통해서, “NO”라는 데이터가 들어오면 BooleanExpresstion의 null값을 주어서 조건을 적용안하게 만들 수 있다.
테스트체크(check) 값마다 조인 조건이 달라진다. 동적 조인 조건 만들기
AdminTestCheck 테이블은 AdminSentence의 FK를 가지고 있다. 그리고 문장id -사용자id 별 그 문장을 맞췄는 지(CORRECT) 틀렸는 지(WRONG) check 데이터를 저장한다.
그런데 여기서 특징은 check 값이 존재하지 않는 문장id-사용자id가 존재한다는 사실이다.
(CORRECT 도 WRONG)아닌 상황(테스트를 아에 보지 않음)에서는 AdminTestCheck에 데이터가 없는 것이다.
아래 SQL Query로 AdminSentence테이블 중심으로 left outer join한 결과를 보면 문장 중에 AdminTestCheck 테이블 정보가 null인 것을 발견할 수 있다.
select sen.id, sen.English, sen.grammar, sen.English, atc.id, atc.user_id, atc.test_check
from admin_sentence sen
left outer join admin_test_check atc
on sen.id = atc.admin_sentence_id
and atc.user_id=5
그래서 페이지 요청 check 정보가
CORRECT일 경우 Inner Join을 사용해서 check 정보가 CORRECT인 것을 가져오고
NO(모든 문장) 정보가 들어올 때는 Left Outer join을 사용해서 모든 문장을 가져온다.
WRONG의 경우 (아직 한번도 테스트를 진행하지 않았거나 틀린 문장)(check 값이 null이거나 WRONG)는 Left Outer join데이터 중에서 check 정보가 CORRECT가 아닌 문장만 가져오도록 했다.
<동적 JOIN 절 만들기(관련된 정보만 실제 코드에서 추출)>
@Slf4j
public class AdminSentenceCustomRepositoryImpl implements AdminSentenceCustomRepository{
JPAQueryFactory queryFactory;
public AdminSentenceCustomRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<Tuple> findTestListByCondition(AdminTestListConditionRequest condition) {
Check check = Check.valueOf(condition.getCheck());
List<Tuple> result = null;
switch (check) {
//CORRECT(테스트 맞은 정보만 가져오기)
case CORRECT:
result = queryFactory
.select(adminSentence.id, adminSentence.korean,
adminSentence.english, adminSentence.grammar, adminSentence.situation,
adminTestCheck.testCheck)
.from(adminSentence)
.where(grammarEq(condition.getGrammar()), situationEq(condition.getSituation()))
.innerJoin(adminSentence.adminTestChecks, adminTestCheck)
//이너 조인을 하고 on 조건에
//해당 userId가 일치하고
//check 정보가 correct인 정보만 가져오기
.on(adminTestCheck.testCheck.eq(check),
adminTestCheck.users.id.eq(condition.getUserId()))
.fetch();
break;
//NO(맞거나 틀리거나 테스트보지않았거나 - 모든 정보 가져오기)
case NO:
result = queryFactory
.select(adminSentence.id, adminSentence.korean,
adminSentence.english, adminSentence.grammar, adminSentence.situation,
adminTestCheck.testCheck)
.from(adminSentence)
.where(grammarEq(condition.getGrammar()), situationEq(condition.getSituation()))
//left outer join으로
//userId가 일치하는 모든 문장 가져온다.
.leftJoin(adminSentence.adminTestChecks, adminTestCheck)
.on(adminTestCheck.users.id.eq(condition.getUserId()))
.fetch();
break;
//WRONG(틀리거나 테스트보지않은 정보만 가져오기)
case WRONG:
result = queryFactory
.select(adminSentence.id, adminSentence.korean,
adminSentence.english, adminSentence.grammar, adminSentence.situation,
adminTestCheck.testCheck)
.from(adminSentence)
.where(grammarEq(condition.getGrammar()), situationEq(condition.getSituation())
, adminTestCheck.isNull().or(adminTestCheck.testCheck.eq(Check.WRONG))
)
//left outer join으로 check 정보가 없는 문장도 전부
//가져오는데,
//가져온 정보 중에 where 조건 절로 check 정보가 CORRECT가
//아닌 정보만 가져오게 설정한다.
.leftJoin(adminSentence.adminTestChecks, adminTestCheck)
.on(adminTestCheck.users.id.eq(condition.getUserId()))
.fetch();
break;
}
return result;
}
'Web Sever 개발과 CS 기초 > 스프링' 카테고리의 다른 글
스프링 알림 기능 - Spring Data JPA DB 구현 (0) | 2023.05.11 |
---|---|
Spring - MySQL과 Querydsl 통계 쿼리 처리(group by, Expressions) (0) | 2023.05.01 |
Querydsl 의 JOIN (INNER, LEFT, RIGHT, THETA, FETCH) 사용법 (0) | 2023.04.29 |
Querydsl 이해와 사용법 (0) | 2023.04.28 |
Spring Data JPA 이해와 사용법 (0) | 2023.04.28 |