Spring/Querydsl

중급 문법

민철킹 2021. 6. 29. 19:46

1. 프로젝션과 결과 반환 

 

1-1. 프로젝션과 결과 반환 - 기본

 

프로젝션 : select 대상을 지정

 

프로젝션 대상이 하나

 

프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있다. 둘 이상이면 튜플이나 DTO로 조회한다.

 

 

튜플 조회

"com.querydsl.core.Tuple"

리스트의 타입이 Tuple형식으로 반환된다. 데이터를 사용할 때는 get()을 사용하여 값을 꺼낸다.

MemberDto

 

1-2. 프로젝션 결과 반환 - DTO 조회 (중요!!)

JPA에서 DTO를 조회할 때는 new 오퍼레이션을 사용하는데, DTO의 패키지명을 다 적어줘야해서 매우 지저분해지고

생성자 방식만 지원한다는 단점이 존재한다.

 

 

 

 

Querydsl 빈 생성

- 프로퍼티 접근

- 필드 직접 접근

- 생성자 사용

 

 

먼저 프로퍼티 접근을 사용하는 방법이다.

Getter, Setter가 필요함(Dto 클래스에)

Setter를 사용하여 bean으로 주입시켜 주는 방식이다.

 

 

필드 직접 접근 방식

이 방식은 필드에 직접 접근하는 방식이기 때문에 @Getter @Setter가 없어도 된다.

 

 

생성자 방식

생성자의 파라미터 타입이 일치하여야 가능하다.

 

 

프로퍼티 명, 필드명이 모두 일치해야만 동작함. 만약 MemberDto의 필드명이 name인데 member.username과 같이 사용하면 매칭되지 않음.

 

이때는 as를 통해 매칭을 시켜준다.

 

서브쿼리 또한 매칭시킬 수가 있다.

ExpressionUtils.as를 사용하여 첫번째 인자로 서브쿼리를 넣고 두번째 인자로 매칭될 필드명을 적어주어 매칭시킨다.

ExpressionUtils.as를 필드에 사용하여도 된다.

 

 

1-3. 프로젝션과 결과 반환 - @QueryProjection

 

 

DTO에 위와 같이 @QueryProjection 애노테이션을 추가하고 complieQuerydsl을 실행하면 DTO도 Q파일로 생성 !!

 

이 방식은 컴파일 이전의 문법오류를 잡아낼 수 있다. Q타입의 클래스의 생성자가 이미 정의되어 있기 때문에

DTO에 QueryDSL 어노테이션을 유지해야 하는 점과 DTO까지 Q 파일을 생성해야 하는 단점이 있다.

==> Querydsl에 대한 의존성의 가짐.

 


2. 동적 쿼리

 

내가 현재 게시판 만들기에 필요한 부분이다.(검색 기능)

where조건에 해당하는 파라미터들이 주어지는데 null인 값은 조건에 포함되지 않도록 할 때 사용하는 방법이다.

2-1. BooleanBuilder 사용

현재 파라미터(username, age)들에 값이 있기 때문에 where 조건으로 바인딩된다.

 

만약 값에 age가 null이라면?

age 파라미터는 where조건에 포함되지 않는 것을 확인할 수 있다. 초기 값을 설정할 수도 있고 builder를 and나 or로 조립할 수 있다.

 

 

2-2. where 다중 파라미터 사용

 

 

파라미터를 확인하는 메서드에서는 삼항연산자를 사용하였다.

Querydsl의 where문에서 null은 무시되기 때문에 만약 null값이 파라미터로 들어오면 ageEq(ageCond)이 null이 되므로 해당 조건은 무시된다.

 

메서드 반환 타입을 BooleanExpression이라면 이를 조합해서 사용이 가능하다.

컴포지션이 가능해지기 때문에 다른 곳에서도 재사용이 가능한 엄청난 이점이 존재한다.

 

 


3. 수정, 삭제 벌크 연산

일반적으로 Dirty Checking을 사용해서 값을 수정하는 경우에는 한 트랜잭션 내에서 변경 사항을 감지하여 수정하게 되는데 이는 객 개별 엔티티마다 발생하기 때문에 쿼리가 여러개가 나간다.

 

하지만 예를 들어, 회원들중에 나이가 20살 이상인 사람들을 수정하는 일이 필요할 때 각 개별 엔티티를 dirty check로 처리하는 것보다 벌크 연산을 사용하여 한번에 처리한다면 성능 부분이 좋아질 수 있다.

 

update문을 사용하여 나이가 28보다 작은 모든 회원의 이름을 비회원으로 변경하는 벌크연산을 수행하였다. 이것의 반환값은 수정된 쿼리 수이다. 현재 상황에서는 28살 이하의 회원이 4명 중 2명이므로 2가 반환될 것이다.

JPA를 공부할 때 배웠지만 벌크연산은 영속성 컨텍스트를 거치지않고 바로 DB에 접근해 update 쿼리를 실행시킨다.

즉, 영속성 컨텍스트에는 member1, member2로 회원이름을 가진 엔티티가 존재하고 DB에는 비회원이라는 이름으로 다른 이름을 가진 동일한 엔티티가 존재하게 되는 것이다.

 

이 상황에서 JPA를 사용해 조회를 하게되면 문제가 발생한다. 영속성 컨텍스트는 1차 캐시의 역할을 하고있다.

SELECT를 하게되면 (전체 조회를 하였다고 가정) DB에 접근하여 엔티티들을 가져온다.

그 뒤 가져온 엔티티가 이미 영속성 컨텍스트에 있는지를 확인하는데(중복을 확인)

동일한 엔티티가 모두 이미 영속성 컨텍스트에 있기 때문에 DB에서 조회한 엔티티들은 모두 버려지고 영속성 컨텍스트에 있는 엔티티들이 반환된다.(항상 영속성 컨텍스트가 우선권을 가짐)

 

실제 확인을 해보면 

이러한 문제를 repeatable read라고한다.

 

이를 위해서는 벌크 연산 이후에 영속성 컨텍스트를 초기화해주자.

벌크 연산을 한 이후에는 어쩔 수 없이 DB와 영속성 컨텍스트는 차이가 존재한다. 영속성 컨텍스트를 초기화가 되었기 때문에 추후에 DB에서 가져온 값이 영속성 컨텍스트에 추가되고 반환되게된다.(이를 위한 애노테이션도 존재!!)

 

모든 회원의 나이를 1살 증가시키는 벌크연산

add 이외에 multiply도 가능하다.(빼기는 add(-1)을 사용하자!)

 

나이가 18살보다 많은 회원 모두 삭제하는 벌크연산

 


4. SQL function 호출

 

SQL function은 JPA와 같이 Dialect(방언)에 등록된 내용만 호출할 수 있다.

 

replace

결과

member가 M으로 대체되어 member1이 M1, member2가 M2.......가 되었다.

 

사용하고있는 DB의 Dialect 클래스를 검색해보면 등록된 SQL 함수들이 나오는데 여기에 등록된 것만 사용할 수 있다.

MySQL의 Dialect이다.

임의로 Dialect를 등록하고 싶을 땐 해당 클래스를 상속받고 설정에 등록을 하여 사용할 수 있다.

 

소문자로 변경

ANSI 표준으로 등록되어있는 함수(거의 모든 DB에서 공통적으로 사용하는 것)들은 Querydsl이 어느정도 내장하고 있으므로 위의 코드를 아래와 같이 변경할 수 있다.

소문자로 변경

 

반응형

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

순수 JPA와 Querydsl  (0) 2021.08.30
기본 문법  (0) 2021.06.18
Querydsl 설정  (0) 2021.06.17
QueryDSL 맛보기  (0) 2021.06.07