https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84
※ ~프로젝트 환경설정 & 예제 도메인 모델과 동작확인
- build.gradle - springdata jpa , spring web , p6spy, h2, lombok, querydsl- jpa, apt 설치
plugins {
id 'org.springframework.boot' version '2.5.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
// querydsl 추가
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
group = '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'
implementation 'com.querydsl:querydsl-apt'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.7.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2:1.4.199'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
// querydsl 추가 시작
def querydslSrcDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslSrcDir
}
sourceSets {
main.java.srcDirs querydslSrcDir
}
// 여기부터 gradle 5.0 이후로 추가
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
// querydsl 추가 끝
- @Commit 안에 @Rollback(value= false)가 있음 (둘이 같음)
※ 기본 문법
ㅁ 시작 - JPQL vs Querydsl
- tasks - other - compileQuerydsl 통해 Q객체 생성
- preparestatement의 parameter binding 방식 사용
> 문자 더하기 방식이 아니라, sql injection 공격 방어 가능
- 컴파일 시점에 오류를 잡을 수 있다. (문법 오류가 발생할 가능성이 적음)
- 위 두가지는 아주 큰 특징이다. (언어의 한계를 해결하기 위해 노력함 )
ㅁ 기본 Q-Type 활용
- Q객체의 기본 static 생성자 및 static import를 활용하자
> 깔끔하게 쓸 수 있음
- use_sql_comments: true 설정하여 주석통해 JQPL 쿼리 확인
- Qmember member = new Qmember("m1") 은 같은 테이블을 조인해야할 때 alias 구분을 위해 선언해서 사용하자
ㅁ 검색 조건 쿼리 & 결과 조회 & 정렬
- .and() 는 , 로 해도됨 (기본 and 적용)
-> 동적 쿼리 만들때 , 가 좋음
- 정렬문 예시
* 회원 정렬 순서
* 1. 회원 나이 내림차순 (desc)
* 2. 회원 이름 올림차순 (asc)
* 단 2에서 회원 이름이 없으면 마지막에 출력(nulls last)
List<Member> result = queryFactory.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
ㅁ 페이징
- offset과 limit로 페이징함
- orderby는 페이징의 필수
- 카운트 쿼리를 따로 분리해야할 수도 있음
> 컨텐츠 쿼리는 어렵고 카운트는 단순할때
> ex) where 문을 쓸 경우, join 등으로 복잡할 경우
ㅁ 집합
- tuple로 count, sum 등을 가져올 수 있음
-> 사실 dto로 정리해서 하는걸 실무에서 함
- live templete의 custom 통해 tdd 작성
- groupby 및 having 사용 가능
ㅁ 조인
- left join은 left outer join
- 그냥 join 은 inner join
- 세타 조인 : 연관관계가 없는 필드로 조인
> join on 으로도 가능
> 카르테시안 곱으로 전체 행 곱하여, 만든 뒤 where 조건에 해당하는 로우만 뽑아냄
> 성능 최적화는 db가 알아서 해줌
ㅁ 조인 - on 절
- ON절을 활용한 조인 ( JPA 2.1부터 지원 )
1. 조인 대상 필터링
2. 연관관계 없는 엔티티 외부 조인
- inner join이면 where 나 on은 똑같음
> 실무에서는 where 사용
- left join(left outer join)은 on으로 해야함
- leftjoin(team) 과 leftjoin(member.team, team) 의 차이
> 후자는 fk 기반으로 체크 후 조인
> 전자는 관계 없이도 조인함
ㅁ 조인 - 페치 조인
- 페치 조인 코드 ( entitymanagerfactory 통해 load 되었는지 확인 )
@PersistenceUnit
EntityManagerFactory emf;
@Test
public void fetchJoinNo() throws Exception {
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1"))
.fetchOne();
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("패치 조인 미적용").isFalse();
}
@Test
public void fetchJoinUse() throws Exception {
em.flush();
em.clear();
Member findMember = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("패치 조인 적용").isTrue();
}
ㅁ 서브 쿼리
- com.querydsl.jpa.jpaexpressions 사용
- 서브쿼리를 위해 q객체 생성 (alias를 통해 구분하기 위해)
- 한계 : JPA JPQL 서브쿼리의 한계점으로 from 서브쿼리(인라인뷰)는 지원하지 않는다. 당연히 Querydsl 도 지원하지 않는다.
> select 이나 where에서 가능
- 해결방안
1. 서브쿼리를 join으로 변경한다.
2. 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
3. nativeSQL을 사용한다.
- from절 안에 from 절이 과연 좋은 쿼리일까? 고민해봐야함
-> 여러번 호출해서 어플리케이션에서 처리하는것은?
ㅁ Case 문
- case 문으로 구현하기 보다는 다 가져온 후, 어플리케이션에서 처리하는것을 권장
ㅁ 상수, 문자 더하기
- Expressions.constant() 로 구현
- 값 합칠 경우, 아래와 같이 사용 (문자 변환할때! 특히 enum 타입)
List<String> result = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue()))
.from(member)
.where(member.username.eq("member1"))
.fetch();
'Spring' 카테고리의 다른 글
[Spring] 실전! Querydsl - 3 (0) | 2021.09.13 |
---|---|
[Spring] 실전! Querydsl - 2 (0) | 2021.09.09 |
[Spring] 실전! 스프링 데이터 JPA - 3 (0) | 2021.09.06 |
[Spring] 실전! 스프링 데이터 JPA - 2 (0) | 2021.09.03 |
[Spring] 실전! 스프링 데이터 JPA - 1 (0) | 2021.09.02 |