Querydsl - 기본문법

JIWOO YUN·2024년 3월 19일
0

Querydsl

목록 보기
2/7
post-custom-banner

기본 조인

  • 첫 파라미터에 조인 대상을 지정 한 후, 두번째 파라미터에 별칭으로 사용할 Q 타입을 지정

    • JPQL 조인이랑 같음.
  • join(조인 대상, 별칭으로 사용할 Q 타입)

기본 조인 사용 테스트

/**
 * 팀 A 에 소속된 모든 회원
 */
@Test
public void join() throws Exception{
    QMember member = QMember.member;
    QTeam team = QTeam.team;

    List<Member> result = queryFactory
            .selectFrom(member)
            .join(member.team, team) //기본 조인
            .where(team.name.eq("teamA"))
            .fetch();

    assertThat(result)
            .extracting("username")
            .containsExactly("member1","member2");
}
  • join(), innerJoin() : 내부 조인(inner join)
  • leftjoin() : left 외부 조인(left outer join)
  • rightJoin() : right 외부 조인(right outer join)
  • JPQL의 on 과 성능 최적화를 위한 fetch 조인 제공

세타 조인

  • 연관관계가 없는 필드로 조인하는 경우
  • from 절에 여러 엔티티를 선택해서 세타 조인
    • 외부 조인이 불가능

세타 조인 테스트

/**
 *  세타 조인(연관 관계가 없는 필드로 조인)
 *  회원의 이름이 팀 이름과 같은 회원 조회
 */
@Test
public void theta_join() throws Exception{
    em.persist(new Member("teamA"));
    em.persist(new Member("teamB"));

    List<Member> result = queryFactory
            .select(member)
            .from(member, team) //세타 조인
            .where(member.username.eq(team.name))
            .fetch();

    assertThat(result)
            .extracting("username")
            .containsExactly("teamA","teamB");
}

조인 - On 절

  • On 절을 활용한 조인
    1. 조인 대상 필터링
    2. 연관관계 없는 엔티티 외부 조인
  1. 조인 대상 필터링

  • 회원과 팀을 조인하고, 팀이름이 teamA인 팀만 조인, 회원은 모두 조회하자.
@Test
public void join_on_filtering() throws Exception{
    List<Tuple> result = queryFactory
            .select(member, team)
            .from(member)
            .leftJoin(member.team, team).on(team.name.eq("teamA"))
            .fetch();

    for (Tuple tuple : result) {
        System.out.println("tuple = " + tuple);
    }

}
  • on 절을 활용해서 조인 대상 필터링시 현재 사용한 외부 조인이 아닌 내부 조인으로 사용하면 where 절에서 필터링 한것과 기능이 동일해진다.
    • on 절을 활용한 조인 대상 필터링을 사용할 때 내부 조인 인경우에는 많이 사용했던 where 절을 통해서 해결 해주고 , 정말 외부 조인이 필요한 경우에만 사용하자.
  1. 연관 관계 없는 엔티티 외부 조인

  • 회원의 이름과 팀의 이름이 같은 대상 외부조인을 해보자.
@Test
public void join_on_no_relation() throws Exception{
    em.persist(new Member("teamA"));
    em.persist(new Member("teamB"));

    List<Tuple> result = queryFactory
            .select(member, team)
            .from(member)
            .leftJoin(team).on(member.username.eq(team.name))
            .fetch();

    for (Tuple tuple : result) {
        System.out.println("t = " + tuple);
    }
}
  • 하이버네이트 5.1 부터 on 을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능 추가 , 내부 조인도 가능하다.

  • 문법의 경우 일반 조인과 다르게 leftjoin에 엔티티가 하나만 들어간다.

  • 일반 조인 : .leftJoin(member.team, team)
    on 조인 : from(member).leftJoin(team).on(member.username.eq(team.name))

페치 조인

  • SQL 에서 제공하는 기능이 아님.
  • JPQL 에서 성능 최적화를 위해서 제공하는 기능
  • 연관된 엔티티나 컬렉션을 한번에 조회하는 기능 Querydsl 사용시 fetch join 명령어로 사용할 수있다.

추가로 참고한 내용 :페치조인(fetch Join )이란 :: 개발의 시작 (tistory.com)

페치조인 사용

@Test
public void fetchJoinUse() throws Exception{
    em.flush();
    em.close();

    Member member1 = queryFactory
            .selectFrom(member)
            .join(member.team, team).fetchJoin()
            .where(member.username.eq("member1"))
            .fetchOne();


    boolean loaded = emf.getPersistenceUnitUtil().isLoaded(member1.getTeam());

    assertThat(loaded).as("페치 조인 적용").isTrue();
}

서브 쿼리

서브 쿼리란?

  • 하나의 SQL 문에 포함되어 있는 또 다른 SQL 문
    • 서브 쿼리를 괄호로 감싸서 사용
    • 단일 행 또는 복수 행 비교 연산자와 함께 사용 가능
    • 서브 쿼리에서는 Order BY 를 사용하지 못함.

참고 사이트 : [DATABASE] 서브쿼리란? 서브쿼리 사용해보기 — 꽁담 (tistory.com)

  • JPAExpressions 사용.
    • static 으로 처리가능.
  • 같은 엔티티를 사용하게 된다면 별칭이 겹치면 안되기 때문에 Qclass 객체를 하나더 생성해서 사용해야한다.

서브 쿼리 eq 에 사용

/**
 * 나이가 가장 많은 회원 조회.
 */
public void subQuery() throws Exception{

    QMember memberSub = new QMember("memberSub");

    List<Member> result = queryFactory
            .selectFrom(member)
            .where(member.age.eq(
                    JPAExpressions
                            .select(memberSub.age.max())
                            .from(memberSub)
            ))
            .fetch();

    assertThat(result).extracting("age")
            .containsExactly(40);
}

from 절 서브 쿼리의 한계

  • JPQL 서브 쿼리의 한계점으로 from 절의 서브쿼리는 지원안하는데 , 결국 querydsl도 JPQL의 빌더기 때문에 지원하지 않게 되는것이다.
  • 하이버네이트 구현체를 사용하면 select 절의 서브 쿼리는 지원
    • querydsl도 하이버 네이트 구현체를 사용하면 select 절의 서브쿼리 지원

해결 방안

  1. 서브 쿼리를 join으로 변경
  2. 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
  3. nativeSQL 사용

CASE 문

  • select ,where, order by 에서 사용 가능.
  • CaseBuilder를 통해서복잡한 케이스문도 가능.

case 기본

@Test
public void basicCase(){
    List<String> result = queryFactory
            .select(member.age
                    .when(10).then("열살")
                    .when(20).then("스무살")
                    .otherwise("기타"))
            .from(member)
            .fetch();

    for (String s : result) {
        System.out.println("s = " + s);
    }
}

복잡한 case 문

@Test
public void complexCase(){
    List<String> result = queryFactory
            .select(new CaseBuilder()
                    .when(member.age.between(0, 20)).then("0~20")
                    .when(member.age.between(21, 30)).then("20~30")
                    .otherwise("기타"))
            .from(member)
            .fetch();

    for (String s : result) {
        System.out.println("s = "  + s);
    }
}
  • 복잡한 케이스문의 경우 굳이 사용하지 않아도 일반적인 case문을 통해서 값을 뽑고 애플리케이션내에서 로직으로 해결하는게 좋긴하다.
    • 복잡한 case 은 결국 DB 쪽에서 부담을 하기 때문에

상수 ,문자 더하기

  • 상수 필요시 Expressions.constant(xxx) 사용

Entity에서 선언되지 않은 필드 일때 상수 결과를 반호나하고 싶을 때 사용된다.

  • Expressions.constant(xxx) : 조회 결과에 사용될 상수 값 지정.
@Test
public void constant(){
    List<Tuple> a = queryFactory
            .select(member.username, Expressions.constant("A"))
            .from(member)
            .fetch();

    for (Tuple tuple : a) {
        System.out.println("tuple = " + tuple);
    }
}

문자 더하기 Concat

@Test
public void concat(){
    String result = queryFactory
            .select(member.username.concat("_").concat(member.age.stringValue()))
            .from(member)
            .where(member.username.eq("member1"))
            .fetchOne();

    System.out.println(result);


}
  • member의 age의 경우 문자가 아니기 때문에 StringValue로 문자로 변환 가능

    • ENUM 타입을 문자로 해서 처리할 때 자주 사용하니 알아두자.
profile
열심히하자
post-custom-banner

0개의 댓글