[Querydsl] 검색 쿼리를 Querydsl로 구현하기(2)
조인
기본 조인
조인은 join(내부 조인), innerJoin(내부 조인), leftJoin(left 외부 조인), rightJoin(right 외부 조인)을 사용할 수 있다.
조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 별칭으로 사용할 Q 타입을 지정하면 된다.
다음은 가장 기본적인 조인 방법이다.
List<Member> result = queryFactory
.selectFrom(member)
.leftJoin(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
세타 조인
세타 조인은 연관관계가 없는 필드로 조인할 경우 사용한다.
List<Member> result = queryFactory
.select(member)
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
from 절에서 여러 엔티티를 선택한다.
외부 조인이 불가능하지만, 조인 on을 사용하면 외부 조인이 가능하다.
조인 - on절
on절은 다음과 같은 기능을 한다.
1) 조인 대상 필터링
2) 연관관계 없는 엔티티 외부 조인(세타 조인)
조인 대상 필터링
쿼리 조건은 다음과 같다.
회원과 팀을 조인하면서, 팀 이름이 'teamA'인 팀만 조인, 회원은 모두 조회
//JQPL
select m, t from Member m left join m.team t on t.name = 'teamA'
//SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='teamA'
//Querydsl
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(member.team, team)
.on(team.name.eq("teamA"))
.fetch();
만약 on절과 내부 조인을 사용할 경우 where 절에서 필터링 하는 것과 기능이 똑같다. 따라서 외부 조인일 때에만 on절을 활용하는 것이 옳바른 방법이다.
연관관계 없는 엔티티 외부 조인
//JPQL
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
//SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
//Querydsl
List<Tuple> result1 = queryFactory
.select(member, team)
.from(member)
.leftJoin(team).on(member.username.eq(team.name))
.fetch();
조인 - 페치 조인
페치 조인은 SQL에서 제공하는 기능은 아니다.
SQL 조인을 활용해 지연로딩으로 설정된 엔티티를 한 번에 조회하는 기능이다. 주로 성능 최적화에 사용하는 방법이다.
Member 엔티티
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
Member 엔티티의 team이 지연 로딩으로 설정된 것을 볼 수 있다.
따라서 페치 조인을 적용하지 않고 일반 조인으로 Member를 조회하면 Team은 조회되지 않는다.
Member findMember = queryFactory
.selectFrom(member)
.where(member.username.eq("member1"))
.fetchOne();
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
assertThat(loaded).as("페치 조인 미적용").isFalse();
그러나 페치 조인을 사용하면 지연 로딩으로 설정된 Team도 조회되는 것을 확인할 수 있다.
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();
JPA의 성능을 최적화하기 위해 대부분의 연관관계는 지연 로딩(LAZY)으로 설정하는 것이 좋다. 만약 즉시 로딩(EAGER)로 설정하면 해당 엔티티를 조인하는 다른 쿼리에서 예상하지 못한 조인이 발생할 수 있기 때문이다.
결론은 대부분의 연관관계는 지연 로딩(LAZY)으로 설정하고, 필요한 경우 페치 조인을 사용해 조회하자!
JPA 관련 글을 작성할 때 더 자세히 다루도록 하겠다.
Case 문
select, where, order by에서 사용 가능하다.
단순한 조건
List<String> result = queryFactory
.select(member.age
.when(10).then("열살")
.when(20).then("스무살")
.otherwise("기타")
)
.from(member)
.fetch();
복잡한 조건
caseBuilder를 사용한다.
List<String> result = queryFactory
.select(new CaseBuilder()
.when(member.age.between(0, 20)).then("0~20")
.when(member.age.between(21, 30)).then("21~30")
.otherwise("기타")
)
.from(member)
.fetch();
orderBy에서 Case 문 함께 사용하기
쿼리 조건은 다음과 같다.
1) 0~30살이 아닌 회원을 가장 먼저 출력
2) 0~20살 회원 출력
3) 21~30살 회원 출력
NumberExpression<Integer> rank = new CaseBuilder()
.when(member.age.between(0, 20)).then(2)
.when(member.age.between(21, 40)).then(3)
.otherwise(1);
List<Tuple> result = queryFactory
.select(member.username, rank)
.from(member)
.orderBy(rank.desc())
.fetch();
Querydsl은 자바 코드로 작성하기 때문에 복잡한 조건을 변수로 선언해 어디서든 사용할 수 있다.
상수, 문자 더하기
상수 추가하기
List<Tuple> result = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetch();
문자 더하기 concat
List<String> result = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue()))
.from(member)
.where(member.username.eq("member1"))
.fetch();
참고
김영한님의 실전! Querydsl
김영한님의 자바 ORM 표준 JPA 프로그래밍
'개발 > JPA' 카테고리의 다른 글
[JPA] 영속성 관리 (0) | 2021.12.08 |
---|---|
[JPA] JPA 소개 (0) | 2021.12.08 |
[Querydsl] 검색 쿼리를 Querydsl로 구현하기(3) (0) | 2021.07.16 |
[Querydsl] 검색 쿼리를 Querydsl로 구현하기(1) (0) | 2021.07.14 |
[Querydsl] 스프링 부트에 Querydsl 설정하기 (0) | 2021.07.14 |
댓글