Spring/Querydsl

기본 문법

민철킹 2021. 6. 18. 18:11

JPQL을 사용하여 회원 이름으로 조회하기 

 

Querydsl을 사용하여 회원 이름으로 조회하기

JPAQueryFactory에 EntityManager 객체를 넣어줌.

훨씬 더 직관성이 있고 알아보기 쉽다. 파라미터 바인딩을 해주지 않아도 알아서 처리해준다.

Querydsl은 JPQL 빌더

JPQL: 문자(실행 시점 오류), Querydsl: 코드(컴파일 시점 오류)

JPQL: 파라미터 바인딩 직접, Querydsl: 파라미터 바인딩 자동 처리

 

EntityManager, JPAQueryFactory 모두 동시성 문제가 발생하지 않게 설계가 되어 있다. 따라서 EntityManager 처럼 JPAQueryFactory도 필드 레벨에서 선언하여 사용하여도 문제없다. 스프링 프레임워크는 여러 쓰레드에서 동시에 같은 EntityManager에 접근해도, 트랜잭션 마다 별도의 영속성 컨텍스트를 제공하기 때문에, 동시성 문제는 걱정하지 않아도 된다.

 


* 데이터 세팅

 

1. 기본 Q-Type 활용

 

Q클래스 인스턴스를 사용하는 2가지 방법

QMember qMember = new QMember("m"); // 별칭 직접 지정
QMember qMember = QMember.member; // 기본 인스턴스 사용

Q클래스에 들어가보면 위와 같이 QMember.member를 사용할 수 있도록 미리 정의되어 있다.

같은 테이블을 조인해야 하는 경우가 아니면 기본 인스턴스를 사용하자

QMember m = QMember.member;

위와 같이 선언을 하지 않고 바로 select(QMember.member)와 같이 사용할 수 있다. 또한 static import를 하게되면 위와 같이 최대로 코드를 축약이 가능해진다.

 

 


2. 검색 조건 쿼리

 

 

한 눈에 보기에도 쿼리문이 무엇을 하는지 파악하기 굉장히 쉽다.

Querydsl은 JPQL의 모든 검색 조건을 제공하고 있다.

member.username.eq("member1") // username = 'member1'
member.username.ne("member1") //username != 'member1'
member.username.eq("member1").not() // username != 'member1'
member.username.isNotNull() //이름이 is not null
member.age.in(10, 20) // age in (10,20)
member.age.notIn(10, 20) // age not in (10, 20)
member.age.between(10,30) //between 10, 30
member.age.goe(30) // age >= 30
member.age.gt(30) // age > 30
member.age.loe(30) // age <= 30
member.age.lt(30) // age < 30
member.username.like("member%") //like 검색
member.username.contains("member") // like ‘%member%’ 검색
member.username.startsWith("member") //like ‘member%’ 검색
...

 

AND 조건을 위와 같이 명시하는 것이 아닌 where문의 파라미터로써 처리할 수 있다.

콤마로 조건을 여러개 넣으면 모두 AND 조건으로 연결된다.

 


3. 결과 조회

 

- fetch() : 리스트 조회, 데이터 없으면 null이 아닌 빈 리스트 반환

- fetchOne() : 단건 조회, 결과 없으면 null, 결과가 2개 이상이면 NonUniqueResultException 발생

- fetchFirst() : limit(1).fetchOne()

- fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행

- fetchCount() : count 쿼리로 변경해서 count 수 조회

 

 

 


4. 정렬

 

 

나이 내림차순 - 이름 올림차순 - 만약 이름이 없으면 맨 뒤에(nullsLast)

 

 


5. 페이징

 

 

 


6. 집합

 

 

위와 같이 단일 타입이 아닌 여러 개의 타입을 조회할 때는 Tuple형식으로 반환된다.

튜플에서 값을 꺼낼 때는 위와 같이 Querydsl코드와 동일한 값으로 꺼낸다. JPQL이 제공하는 모든 집합 함수를 제공한다.

tuple.get(member.count())

튜플을 사용하기 보다는 DTO로 직접 뽑아오는 방식을 많이 사용한다.

 

 

6-1. GroupBy

팀의 이름과 각 팀의 평균 연령을 구하는 쿼리를 작성하였다.

추가로 having조건도 포함하여 쿼리를 작성할 수 있다.

 


7. 조인 

 

7-1. 기본 조인

teamA에 소속된 모든 회원을 조회하는 쿼리를 작성하였다. @BeforeEach를 통해 들어가있는 teamA 소속 회원은

member1과 member2이다.

leftJoin과 rightJoin도 사용가능하다!

 

 

세타 조인

세타 조인이란 연관 관계가 없는 필드로 조인하는 것이다.

연관 관계가 없는 회원 이름과 팀 이름을 세타 조인으로 쿼리 작성하였다.

이는 모든 회원과 모든 팀을 조회한 다음 전부 조인하고 where절에서 필터링하여 결과를 추출한다. DB에서 성능 최적화를 해주기 때문에 바로 사용해도 괜찮다.

from 절에 엔티티들을 나열하여 세타 조인을 한다.(모든 필드를 조인함 ==> 카테시안 곱)

하지만 이는 외부 조인이 불가능하다.(Left Outer Join, Right Outer Join).

조인 on절을 사용하면 연관 관계가 없는 테이블도 외부 조인이 가능해진다.

 

 

 

 

7-2. ON절

 

ON절을 활용한 조인 ==> 조인 대상 필터링, 연관관계 없는 엔티티 외부 조인

 

1. 조인 대상 필터링

회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
JPQL : select m, t from Member m left join m.team t on t.name = 'teamA'

Left Outer Join을 하였기 때문에 ON절에 해당하지 않는 팀에 대한 정보는 null이 담겨있음을 확인할 수 있다.

 

ON절을 INNER JOIN에서 사용하면 WHERE절로 필터링한 결과와 동일하다. 따라서 ON절은 외부조인을 할 때만 사용하자.(INNER JOIN을 쓸 때는 그냥 WHERE절을 사용)

 

 

 

2. 연관관계 없는 엔티티 외부 조인

SQL 문법을 잘 보면 leftJoin 부분에 일반 조인과 다르게 엔티티 하나만 들어간다.(위와 비교)

 

 

7-3. Fetch Join

 

페치 조인은 SQL에서 제공하는 기능이 아닌 N+1 문제를 해결하기 위한 성능 최적화에 사용하는 방법이다.

 

현재 LAZY 로딩으로 설정되어 있기 때문에 아래와 같이 Member를 조회하면 Team은 Proxy객체로 조회되고 실제로 조회된 상태는 아니다.

EntityMangerFactory를 사용하여 현재 Team엔티티가 로딩되었는지를 검사해보면 false가 반환된다.

 

Fetch Join 사용

페치 조인을 사용하는 법은 위와 같다. 이제는 한방 쿼리로 모두 조회하였기 떄문에 isLoaded가 true로 반환된다.

Fetch Join 사용한 쿼리문

 

 


8. 서브 쿼리

 

com.querydsl.jpa.JPAExpressions 사용

 

나이가 가장 많은 회원 조회

서브쿼리를 사용하므로 alias가 외부에서 사용하는 것과 동일해선 안된다. 따라서 memberSub라는 QMember를 하나 생성하여 서브쿼리를 사용하였다.

 

나이가 평균 이상인 회원 조회

 

 

IN절 사용하여 10살보다 나이가 많은 회원 조회

 

 

SELECT 절에서 서브쿼리 사용

 

(참고로 JPAExpressions는 static import가 가능하다.)

 

JPA, JPQL 서브쿼리의 한계점

JPA, JPQL 서브쿼리는 from절의 서브쿼리(인라인 뷰)는 지원하지 않는다. Querydsl도 지원 X

 

해결 방안

서브쿼리를 join으로 변경하여 사용(가능하지 않은 상황도 존재) ==> 대부분 가능한 상황이 많음

애플리케이션에서 쿼리를 2번 분리해서 실행

nativeSQL을 사용

 

DB는 순수하게 데이터를 꺼내오는 용으로만 사용하는 것이 좋다. 
FROM절 서브쿼리처럼 데이터를 가공하는 것이 아니라 애플리케이션에서 이를 가공하고 로직으로 처리하는 것이 더 좋은 방식의 설계이다.(화면에 맞추기 위해 쿼리를 짜는 것은 좋지 않다는 의미) 가져온 데이터를 가공하여 화면에 제공하는 것이 쿼리의 재사용성도 높아지고 훨씬 더 좋은 방식이다.

하지만 모두 상황에 따라 다르기 때문에 좋은 판단을 내릴 수 있어야한다!

 

 


9. Case 문

 

select, 조건절에서 사용 가능

 

간단한 조건

when, then, otherwise를 통해 간단한 조건을 처리할 수 있다.

 

 

복잡한 조건

복잡한 case는 CaseBuilder를 사용한다.

 

필요할 때도 있지만 이것도 마찬가지로 데이터를 가져온 다음 애플리케이션에서 로직에 맞게 처리하는 것이 더 좋다.

 


10. 상수, 문자 더하기

 

상수가 필요하면 Expressions.constant사용

 

문자 더하기는 concat을 사용한다.

이름_나이 와 같은 형식으로 문자를 합쳤다. concat은 문자만 가능한데 age는 int이기 때문에 stringValue()를 사용하였다.

문자열이 아닌 타입을 문자열로 변환할 때 stringValue()를 사용하는데 Enum타입을 처리할 때 유용하다!!

 

반응형

'Spring > Querydsl' 카테고리의 다른 글

순수 JPA와 Querydsl  (0) 2021.08.30
중급 문법  (0) 2021.06.29
Querydsl 설정  (0) 2021.06.17
QueryDSL 맛보기  (0) 2021.06.07