(spring)(실전! Querydsl_01)

전성영·2022년 7월 21일
1

spring

목록 보기
23/31

QueryDsl 프로젝트 셋팅

build.gradle

//추가
buildscript {
	ext {
		queryDslVersion = "5.0.0"
	}
}

plugins {
	id 'org.springframework.boot' version '2.7.1'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	//querydsl 추가
	id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
	id 'java'
}

group = 'com.study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	//querydsl 추가
	implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
	implementation "com.querydsl:querydsl-apt:${queryDslVersion}"

	//쿼리 파라미터 로그 남기기
	implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0'

	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
	jpa = true
	querydslSourcesDir = querydslDir
}
sourceSets {
	main.java.srcDir querydslDir
}
configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
	querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
	options.annotationProcessorPath = configurations.querydsl
}
//querydsl 추가 끝

추가된 부분은 주석으로 표시해놨다.
버전 문제로 몇 가지 더 추가 해놓았다.


그 후 밑에 순서대로 진행해준다.

  • Gradle -> build -> clean
  • Gradle -> other -> compileQueryDsl

실행하면, build.gradle 에서 설정한 경로('def querydslDir = "$buildDir/generated/querydsl"')에 파일이 생성된다.

Q+엔티티 이름의 클래스가 생성되면 정상적으로 QueryDSL이 설치가 완료된 것이다.


예제 도메인 모델

! 엔티티에서 @ToString 어노테이션을 사용할 때 주의할 점!
연관관계가 맺어진 컬럼은 빼야한다.
그렇지 않으면 m -> t , t -> m, m -> t 이런식으로 된다.

JPQL vs Querydsl

jpql

Querydsl

아직까진 JPQL이 눈에 더 잘 띄는 거 같다.
Querydsl Q클래스 인스턴스를 사용하는 방법은 총 3 가지 있다.

1. QMember m = new QMember("m"); //별칭 직접 지정
2. QMember m = QMember.member; //기본 인스턴스 사용
3. static Import 사용하기


.
.
Alt + Enter 후 변경해주면
.
.

얘를 바로 호출해 주는 것이다.
QMember

!같은 테이블을 조인해야 하는 경우에만 2번 방법으로 진행하고 그 이외에는 static import 사용을 하자!!


검색조건

신기하게 생겼다... goe, gt 등등.. 최댛나 빨리 눈에 익혀야겠다!

찾아보니 limit함수도 제공해주는 것 같았다. 신기방구

또한 QueryDsl 초보자로서 끝맺음에 항상 fetch~ 가 들어갔는데 강의에서는 양과 질을 다 챙겨야 되기 때문에 설명을 안 넣으신 것 같았다. 내가 못들었을 수도;;

  • fetch() : 리스트로 결과를 반환하는 방법이다.
    (만약에 데이터가 없으면 빈 리스트를 반환해준다.)
    .
  • fetchOne() : 단건을 조회할 때 사용하는 방법이다.
    (결과가 없을때는 null 을 반환하고 결과가 둘 이상일 경우에는 NonUniqueResultException을 던집니다.)
    .
  • fetchFirst(): 처음의 한건을 쿼리해서 가져오고 싶을때 사용한다.
    (limit(1).fetchOne())
    .
  • fetchResults() : 해당 내용은 페이징을 위해 사용될 수 있습니다. 페이징을 위해서 total contents를 가져온다.
    .
  • fetchCount() : count 쿼리를 날릴 수 있다.

(추가) 아... 바로 다음 강의에서 다뤄주시는구나ㅋㅋㅋㅋㅋㅋㅋㅋㅋ


위에랑 아래랑 같다.
.and와 , 는 같은 역할을 하는 것이다.
,는 중간에 null이 나오면 무시한다고 한다. 동적 쿼리에 유리할 것 같은데 바로 학습해서 정리해야 겠다!


결과 조회

위에 정리해 놓은 것을 참고하면 된다.

이 두가지는 흥미로워서 추가!


정렬


member.age 로 접근을 한 다음 .desc()로 내림차순을 바로 적용할 수 있다는게 놀라웠다.
nullsLast() 는 null이면 마지막에 출력 시키는 함수이다.
nullsFirst() 도 있다는 것을 알아두자!!


페이징


이런식으로 offsetlimit가 적용된다.
offset는 몇 번째 부터 출력할 것인지 정하는 것이다.
limit는 갯수를 정해두는 것이라고 생각하면 편하다.


fetchResults()를 사용하면 getTotal, ..limit, ..offset, ..results().size() 등 을 알 수 있다!
Query는 총 두 번 나간다.
카운트 쿼리가 추가가 된 것이다.


정렬

select 부분에 member.age.avg()중 .avg()를 빼준 후 돌리면 돌아가지 않는다.
주의하자!!

또한 Having절은 groupBy절 뒤에 사용한다.
groupBy절로 묶고 select 부분에서 집계함수를 사용하는데, Having절로 조건을 거는 것 같다.


조인

join

@Query어노테이션을 사용한 JPQL에서 조인을 할 때와 비슷하다.

theta join

from절이 간단해졌다. 일명 막조인
단점은 외부조인(left, right)가 안된다는 것인데, on절을 통해서 극복이 가능하다!

.extracting - 특정 컬럼을 추출
containsExactly - 순서를 포함해서 정확히 일치하는지.
contaion 관련 함수는 몇 가지 더 있다.


조인 - on절

on과 where 차이


출처 : https://eddyplusit.tistory.com/52
https://myjamong.tistory.com/229


전체 코드

join

left join

right join

외부 조인이 아닌 내부 조인인 경우 ON 대신 WHERE을 사용하자!


연관관계가 없는 엔티티 외부조인

달라진 점은 leftjoin쪽이다.
원래는 (member.team, team) 이런식으로 접근을 해서 자동적으로 on절에 team의 id값이 들어가는데, 상대는 막조인이다.
고로 team을 그냥 넣어주면 된다!


페치 조인


.fetchJoin()을 통해 페치 조인을 해줄 수 있다.

@PersistenceUnit
EntityManagerFactory emf;

emf.getPersistenceUnitUtil() 을 사용하여 검증을 해준다.

서브쿼리

1.

서브쿼리를 사용하려면 JPAEXperssions를 사용하면 된다.

밑에 Assertions 쪽은 검증하는 곳이다. 전에 다룬적이 있다.

그리고 메인 쿼리랑 서브 쿼리랑 member가 곂치면 안 되기 때문에 memberSub 객체를 생성해준 것이다.

2.

서브쿼리 + in절을 사용할 수도 있다.

3.

또한 select절에서도 서브쿼리를 사용할 수 있다.

!!JPAExpressions도 static import가 가능하다 alt + enter 고고고!!

from 절의 서브쿼리 한계

JPA JPQL 서브쿼리의 한계점으로 from 절의 서브쿼리(인라인 뷰)는 지원하지 않는다. 당연히 Querydsl 도 지원하지 않는다.
하이버네이트 구현체를 사용하면 select 절의 서브쿼리는 지원한다. Querydsl도 하이버네이트 구현체를 사용하면 select 절의 서브쿼리를 지원한다.

  • from 절의 서브쿼리 해결방안
  1. 서브쿼리를 join으로 변경한다. (가능한 상황도 있고, 불가능한 상황도 있다.)
  2. 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
  3. nativeSQL을 사용한다

CASE

이렇게 case문을 입힐 수 있다.
but 복잡해진다면??

CaseBuilder()를 사용하면 된다!!

상수, 문자 더하기

상수

Expressions.constant를 사용하면 된다.

서브쿼리할 때 사용했던 JPAExpressions 와 비슷하니깐 노헷갈!!!


문자 더하기

concat()을 사용해주는데 age는 int형 이니깐 stringValue()로 형변환을 해준다.
stringValue()는 enum 클래스에 접근할 때 자주 쓰인다고 한다!!

여기서 마치고 중급 문법 부터는 2편에 정리할 예정이다.
뾰ㅕ로로롤ㅇ

profile
Slow and Steady

0개의 댓글