ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Querydsl (1) - 기본 문법
    Web/QueryDSL 2024. 1. 24. 15:08

     

    1. QueryDSL 기본 사용법 

     

    public void startQuerydsl() {
     //member1을 찾아라.
     JPAQueryFactory queryFactory = new JPAQueryFactory(em);
     QMember m = new QMember("m");
     Member findMember = queryFactory
     .select(m)
     .from(m)
     .where(m.username.eq("member1"))//파라미터 바인딩 처리
     .fetchOne();
     assertThat(findMember.getUsername()).isEqualTo("member1");
    }

     

    - QMember는 querydsl 라이브러리 추가하고, 엔티티를 한번 빌드하면 생긴다.

      (별칭은 jpql에서 사용할 별칭 -> 내부조인하는거아니면 잘 사용하지 않는다)

    - JPAQueryFactory에 EntityManager를 넣고 사용한다. (엔티티 메니저는 빈으로 등록해서 사용해도 된다!)

     

     

     

    2. 기본 Q-Type 활용

    QMember qMember = new QMember("m"); //별칭 직접 지정
    QMember qMember = QMember.member; //기본 인스턴스 사용
    Member findMember = queryFactory
     .select(member)
     .from(member)
     .where(member.username.eq("member1"))
     .fetchOne();
     
     //querydsl은 결국 jpql을 자동으로 만들어주는 라이브러리이다!

     

    2.2 검색 조건 쿼리 

     

    Member findMember = queryFactory
     .selectFrom(member)
     .where(member.username.eq("member1")
     .and(member.age.eq(10)))
     .fetchOne();

     

    - where뒤에 and(조건),or(조건)으로 연결할 수 있다. 

    - where()안에 and조건을 ,로 구분해서 넣을 수도 있다! (null이면 자동으로 무시)

     

    //jpql의 모든 검색 조건을 제공한다!
    member.username.eq("member1") // username = 'member1'
    member.username.ne("member1") //username != 'member1'
    member.username.eq("member1").not() // username != 'member1'
    member.username.isNotNull() //이름이 is not null
    member.age.in(10, 20) // age in (10,20)
    member.age.notIn(10, 20) // age not in (10, 20)
    member.age.between(10,30) //between 10, 30
    member.age.goe(30) // age >= 30
    member.age.gt(30) // age > 30
    member.age.loe(30) // age <= 30
    member.age.lt(30) // age < 30
    member.username.like("member%") //like 검색
    member.username.contains("member") // like ‘%member%’ 검색
    member.username.startsWith("member") //like ‘member%’ 검색

     

    2.3 결과 조회 

     

    - fetch() : 리스트 조회, 데이터 없으면 빈 리스트 반환

    - fetchOne() : 단 건 조회 (결과 없으면 null, 결과 둘 이상이면 NonUniqueResultException)

    - fetchFirst(): limit(1).fetchOne()과 같다.

    - fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행 

    - fetchCount() : count 쿼리로 변경해서 count 조회 

     

    2.4 정렬 

     

    List<Member> result = queryFactory
     .selectFrom(member)
     .where(member.age.eq(100))
     .orderBy(member.age.desc(), member.username.asc().nullsLast())
     .fetch();

     

    - desc(), asc() : 정렬

    - nullsLast(), nullsFirst() : null 데이터 순서 부여

     

     

    3. 페이징 

     

    List<Member> result = queryFactory
     .selectFrom(member)
     .orderBy(member.username.desc())
     .offset(1) //0부터 시작(zero index) 앞에 건너뛰고 싶은 갯수
     .limit(2) //최대 2건 조회 페이지당 갯수 -> limit기준으로 page 나온다.
     .fetch();

     

    * 데이터를 조회하는 쿼리는 여러 테이블 조인한다. count쿼리는 조인이 필요없는 경우도 있다.

       하지만, 자동화된 count쿼리는 원본 쿼리와 모두 조인하기때문에 성능이 안나올 수 있다. count에 최적화 필요시 count전용 필드 만들자.

     

    4. sum,avg,max,min등의 함수

    List<Tuple> result = queryFactory
     .select(member.count(),
     member.age.sum(),
     member.age.avg(),
     member.age.max(),
     member.age.min())
     .from(member)
     .fetch();

     

    -결과가 튜플 자료형이다. tuple.get(member.count()), tuple.get(member.age.sum())으로 가져올 수 있다.

     

    5. GroupBy 

     

    List<Tuple> result = queryFactory
     .select(team.name, member.age.avg())
     .from(member)
     .join(member.team, team)
     .groupBy(team.name)
     .fetch();

    - having도 사용가능하다! 

     

    6. 조인 

     

    6.1 기본 조인 

     

    - >내부 조인

     

    기본 저인은 첫 번째 파라미터에 조인 대상 지정하고, 두번째 파라미터에 별칭으로 사용할 Q타입 지정하면됨

    QMember member = QMember.member;
     QTeam team = QTeam.team;
     List<Member> result = queryFactory
     .selectFrom(member)
     .join(member.team, team)
     .where(team.name.eq("teamA"))
     .fetch();

     

    - 기본적으로 fk를 사용해서 조인해준다! 

     

    - join(), innerJoin(), leftJoin(),rightJoin() 성능 최적화를 위한 fetch조인 모두 제공 

     

    -> on절의 활용 

     

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

     

    -> on절을 통해 join할 테이블의 값에 where 조건을 걸 수 있다. 만약 inner 조인이면 그냥 where 조건문에서 필터링하자

     

    6.2 연관 관계 없는 필드 조인 

     

    -> 연관관계 없는 필드로 조인  (inner) 세타조인

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

     

     - from절을 사용해서 조인한다. 

     

    -> 연관관계 없는 엔티티 외부 조인 

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

     

    - leftJoin, rightJoin (조인할 대상만 Q타입으로 가져옴 -> 내 필드에 값 사용 x 어차피 연관관계 없음)

    - on으로 필터 조건을 건다.

     

     

    6.3 패치조인 

     

    - 패치조인은 SQL에서 제공하는 기능이 아니다. SQL조인을 활용해서 연관된 엔티티를 한번에 조회하는 최적화 기능

     

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

     

     

    7. 서브 쿼리 

     

    7.1 where 절에서 사용 

     

    QMember memberSub = new QMember("memberSub");
     List<Member> result = queryFactory
     .selectFrom(member)
     .where(member.age.eq(
     JPAExpressions
     .select(memberSub.age.max())
     .from(memberSub)
     ))
     .fetch();
     
     
     List<Member> result = queryFactory
     .selectFrom(member)
     .where(member.age.goe(
     JPAExpressions
     .select(memberSub.age.avg())
     .from(memberSub)
     ))
     .fetch();
     
     
     List<Member> result = queryFactory
     .selectFrom(member)
     .where(member.age.in(
     JPAExpressions
     .select(memberSub.age)
     .from(memberSub)
     .where(memberSub.age.gt(10))
     ))
     .fetch();

     

    - JPAExpressions를 한번 사용해줘야한다.

     

    7.2 select절에서 사용 

     

    List<Tuple> fetch = queryFactory
     .select(member.username,
     JPAExpressions
     .select(memberSub.age.avg())
     .from(memberSub)
     ).from(member)
     .fetch();

     

     

    * from절에서 서브쿼리 사용하는 인라인뷰는 지원하지 않는다.

     

    > 조인으로 해결한다. 어플리케이션에서 쿼리를 2번 분리한다. nativeSQL을 사용한다! 

    > 보통 프롬절 서브쿼리는 화면에 보여지는 거 때문에 복잡해지는 경우가 많다. 화면에 맞춘 쿼리를 날리면 재사용성이 떨어진다. 데이터는 딱 필요한 것만 퍼올리고, 프레젠 테이션 로직은 프레젠테이션에서 끝내는게 맞다.

    꼭 한방 쿼리로 가져와야한다고 생각하지말자!

     

     

    8. Case문 

    List<String> result = queryFactory
     .select(member.age
     .when(10).then("열살")
     .when(20).then("스무살")
     .otherwise("기타"))
     .from(member)
     .fetch();
     
     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();

     

    - when안에서 조건을 쉽게 넣을 수 있다!

     

    * 만약 임의의 순서로 회원을 출력하고 싶다면?

     

    NumberExpression<Integer> rankPath = new CaseBuilder()
     .when(member.age.between(0, 20)).then(2)
     .when(member.age.between(21, 30)).then(1)
     .otherwise(3);
    List<Tuple> result = queryFactory
     .select(member.username, member.age, rankPath)
     .from(member)
     .orderBy(rankPath.desc())
     .fetch();

     

    - NumberExpression을 통해 rankPath처럼 복잡한 변수를 만들어서 select, orderBy절에서 함께 사용할 수 있다.

     

    9. 상수 문자 더하기 

     

    Tuple result = queryFactory
     .select(member.username, Expressions.constant("A"))
     .from(member)
     .fetchFirst();

     

    - 이때 그냥 상수라면, 쿼리에 포함 x 그냥 결과에 붙여준다.

     

    9.1 문자 더하기 concat

     

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

     

    - .concat(더하기 문자열)하면된다. 

    - stringValue()로 문자로 직접 변환 가능하다. ENUM처리시 자주 사용함

    'Web > QueryDSL' 카테고리의 다른 글

    Querydsl (4) - 스프링 데이터 JPA + Querydsl  (0) 2024.01.24
    Querydsl (3) - 순수 JPA와 Querydsl  (0) 2024.01.24
Designed by Tistory.