Querydsl

[실전! Querydsl] 03. 기본 문법

kittity 2024. 7. 30. 00:22

실전! Querydsl 강의를 듣고 정리한 내용입니다.
➡️ 강의 : https://www.inflearn.com/course/querydsl-실전/dashboard

➡️ 코드 : https://github.com/leehanna602/querydsl

 

실전! Querydsl 강의 | 김영한 - 인프런

김영한 | Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, 복잡한 쿼리, 동적 쿼리는 이제 안녕! Querydsl로 자바 백엔드 기술을 단단하게. 🚩 본 강의는 로드맵 과정입니다. 본 강의는 자

www.inflearn.com

 

1. JPQL vs Querydsl

  • JPQL과 Querydsl의 차이점
    1. 오류 발견 시점
      • JPQL : 실행 시점
      • Querydsl : 컴파일 시점
    2. 파라미터 바인딩
      • JPQL : 직접 파라미터 바인딩
      • Querydsl : 자동으로 파라미터 바인딩
    • 참고 코드
      @SpringBootTest
      @Transactional
      public class QuerydslBasicTest {
          @Autowired
          EntityManager em;
      
          JPAQueryFactory queryFactory;
      
          @BeforeEach
          public void before() {
              queryFactory = new JPAQueryFactory(em);
              Team teamA = new Team("teamA");
              Team teamB = new Team("teamB");
              em.persist(teamA);
              em.persist(teamB);
      
              Member member1 = new Member("member1", 10, teamA);
              Member member2 = new Member("member2", 20, teamA);
      
              Member member3 = new Member("member3", 30, teamB);
              Member member4 = new Member("member4", 40, teamB);
      
              em.persist(member1);
              em.persist(member2);
              em.persist(member3);
              em.persist(member4);
          }
      
          @Test
          public void startJPQL() {
              // member1을 찾아라
              String qlString =
                      "select m from Member m " +
                      "where m.username = :username";
              Member findMember = em.createQuery(qlString, Member.class)
                      .setParameter("username", "member1")
                      .getSingleResult();
      
              assertThat(findMember.getUsername()).isEqualTo("member1");
          }
      
          /* Querydsl의 장점
          *  - 컴파일 시점 오류 발견 가능 (쿼리)
          *  - 파라미터 바인딩이 자동으로 됨
          */
          @Test
          public void startQuerydsl() {
              QMember m = new QMember("m");
              
              Member findMember = queryFactory
                      .select(m)
                      .from(m)
                      .where(m.username.eq("member1")) // 파라미터 바인딩이 자동으로 됨 - JPQL과 차이
                      .fetchOne();
      
              assertThat(findMember.getUsername()).isEqualTo("member1");
          }
      
      }


2. 기본 Q-Type 활용

  • Q-Type : Query Type, 쿼리용 클래스 생성
  • Q클래스 인스턴스 사용하는 2가지 방법
    1. 별칭 직접 입력
      • QMember m = new QMember(”m”);
    2. 기본 인스턴스 사용 + static import 사용 (권장)
      • QMember.member 이용
      • 같은 테이블을 조인해야 하는 경우(alias 다르게 직접 지정)가 아니면 기본 인스턴스(QMember에 정의된 alias 사용)를 사용하자 → 아래 설정으로 로그 보면 확인 가능
    • 참고 코드
      import static study.querydsl.entity.QMember.member;
      ...
      	@Test
          public void startQuerydsl() {
               /* Q클래스 인스턴스를 사용하는 2가지 방법
               *  1. 별칭 직접 지정
               *  2. 기본 인스턴스 사용 + static (권장)
               *  - 같은 테이블을 조인해야하는 경우가 아니면 기본 인스턴스 사용하기
               *  - 실행되는 JPQL을 보면 alias가 QMember에 기본 설정된 값 사용됨
               *  - select member1 from Member member1 ...
               */
      //        QMember m = new QMember("m"); // 별칭 직접 지정
      //        QMember m = QMember.member; // 기본 인스턴스 사용 -> static import로 사용하면 코드 더 줄어든다 (권장)
              
              Member findMember = queryFactory
                      .select(member)
                      .from(member)
                      .where(member.username.eq("member1")) // 파라미터 바인딩이 자동으로 됨 - JPQL과 차이
                      .fetchOne();
      
              assertThat(findMember.getUsername()).isEqualTo("member1");
          }
  • Querydsl에서 실행되는 JPQL 출력 설정
    • spring.jpa.properties.hibernate.use_sql_comments: true

 

3. 검색 조건 쿼리

  • point
    1. select, from -> selectFrom 가능
    2. and() 대신 , 가능
      • 파라미터로 처리할 경우 장점 -> null값은 무시 -> 메서드 추출을 활용해서 동적 쿼리 깔끔하게 만들 수 있음
    3. 그외.. JPQL이 제공하는 모든 검색 조건 제공 → 참고 코드
    • 참고코드
      @Test
      public void search() {
          Member findMember = queryFactory
                  .selectFrom(member) // select, from -> selectFrom 가능
                  .where(
                          // and(), or() 로 연결
                          member.username.eq("member1").and(member.age.eq(10))
                  )
                  .fetchOne();
      
          assertThat(findMember.getUsername()).isEqualTo("member1");
      }
      
      @Test
      public void searchAndParam() {
          Member findMember = queryFactory
                  .selectFrom(member) // select, from -> selectFrom 가능
                  .where(
                          // and() 대신 , 가능
                          // 파라미터로 처리할 경우 장점 -> null값은 무시 -> 메서드 추출을 활용해서 동적 쿼리 깔끔하게 만들 수 있음
                          member.username.eq("member1"), (member.age.eq(10))
                  )
                  .fetchOne();
      
          assertThat(findMember.getUsername()).isEqualTo("member1");
      }
      
      @Test
      public void searchTest() {
          Member findMember = queryFactory
                  .selectFrom(member)
                  .where(
      //                        member.username.eq("member1")
      //                        member.username.ne("member1")
      //                        member.username.eq("member1").not()
      
      //                        member.username.isNotNull()
      
      //                        member.age.in(10, 20)
      //                        member.age.notIn(10, 20)
      //                        member.age.between(10, 30)
      
      //                        member.age.goe(30) // age >= 30
      //                        member.age.gt(30) // age > 30
      //                        member.age.loe(20)
                          member.age.lt(20)
      
      //                        member.username.like("member%")
      //                        member.username.contains("member")
      //                        member.username.startsWith("member")
                  )
                  .fetchOne();
          assertThat(findMember.getUsername()).isEqualTo("member1");
      }


4. 결과 조회

    • 결과조회
      • fetch() : 리스트 조회, 없으면 빈 리스트 반환
      • fetchOne() : 단건 조회
        • 결과가 없으면 : null
        • 결과가 둘 이상이면 : com.querydsl.core.NonUniqueResultException
      • fetchFirst() : 처음 한 건 조회
        • limit(1).fetchOne()
      • count 쿼리
        • count(*) → .select(Wildcard.*count*)
        • count(member.id) → .select(*member*.count())
      • 참고코드
        @Test
        public void resultFetch() {
            // List 조회 (데이터 없으면 빈 리스트 반환)
            List<Member> fetch = queryFactory
                    .selectFrom(member)
                    .fetch();
        
            /* 단 건 조회
            *   - 결과가 없으면 : null
            *   - 결과가 둘 이상이면 : com.querydsl.core.NonUniqueResultException
            */
            Member fetchOne = queryFactory
                    .selectFrom(member)
                    .where(member.username.eq("member1"))
                    .fetchOne();
        
            // 처음 한 건 조회 - fetchFirst() => limit(1).fetchOne()
            Member fetchFirst = queryFactory
                    .selectFrom(member)
                    .fetchFirst();
        
            // count(*) 쿼리 - 응답 결과는 숫자 하나이므로 fetchOne() 을 사용
            Long totalCount = queryFactory
                    .select(Wildcard.count)
                    .from(member)
                    .fetchOne();
            System.out.println("totalCount = " + totalCount);
        
            // count(member.id) 쿼리
            Long totalMemberCount = queryFactory
                    .select(member.count())
                    .from(member)
                    .fetchOne();
            System.out.println("totalMemberCount = " + totalMemberCount);
        }

 

5. 정렬

  • 정렬
    • desc() , asc() : 일반 정렬
    • nullsLast() , nullsFirst() : null 데이터 순서 부여
  • 참고코드
    /**
     * 회원 정렬 순서
     * 1. 회원 나이 내림차순(desc)
     * 2. 회원 이름 올림차순(asc)
     * 3. 단 2에서 회원이름이 없으면 마지막에 출력(nulls last)
     */
    @Test
    public void sort() {
        em.persist(new Member(null, 100));
        em.persist(new Member("member5", 100));
        em.persist(new Member("member6", 100));
    
        /* 정렬
        *   - desc() , asc() : 일반 정렬
        *   - nullsLast() , nullsFirst() : null 데이터 순서 부여
        */
        List<Member> result = queryFactory
                .selectFrom(member)
                .where(member.age.eq(100))
                .orderBy(member.age.desc(), member.username.asc().nullsLast())
                .fetch();
    
        Member member5 = result.get(0);
        Member member6 = result.get(1);
        Member memberNull = result.get(2);
        assertThat(member5.getUsername()).isEqualTo("member5");
        assertThat(member6.getUsername()).isEqualTo("member6");
        assertThat(memberNull.getUsername()).isNull();
    }

 

6. 페이징

  • 페이징
    • .offset() : 앞에 몇개를 스킵할지 0부터 시작, 1이면 하나를 스킵
    • .limit() : 최대 조회 개수
  • 참고코드
    @Test
    public void paging1() {
        List<Member> result = queryFactory
                .selectFrom(member)
                .orderBy(member.username.desc())
                .offset(1) // 앞에 몇개를 스킵할지 0부터 시작, 1이면 하나를 스킵
                .limit(2)
                .fetch();
    
        assertThat(result.size()).isEqualTo(2);
    }

 

7. 집합

  • 집합 함수
    • JPQL이 제공하는 모든 집합 함수를 제공
  • 참고 코드
    @Test
    public void aggregation() {
         /* Tuple
         *  - member 단일 타입 조회가 아니라 데이터 타입이 여러개일 때 tuple 사용
         *  - 실무에서는 주로 Tuple 보다는 DTO 사용
         */
        List<Tuple> result = queryFactory
                .select(member.count(),
                        member.age.sum(),
                        member.age.avg(),
                        member.age.max(),
                        member.age.min())
                .from(member)
                .fetch();
    
        Tuple tuple = result.get(0);
        assertThat(tuple.get(member.count())).isEqualTo(4);
        assertThat(tuple.get(member.age.sum())).isEqualTo(100);
        assertThat(tuple.get(member.age.avg())).isEqualTo(25);
        assertThat(tuple.get(member.age.max())).isEqualTo(40);
        assertThat(tuple.get(member.age.min())).isEqualTo(10);
    }
  • GroupBy 사용
    • groupBy에서 그룹화된 결과를 제한하려면 having
      • 예시
        ...
        .groupBy(item.price)
        .having(item.price.gt(1000))
        ...
    • 참고 코드
      /**
       * 팀의 이름과 각 팀의 평균 연령을 구해라.
       */
      @Test
      public void group() throws Exception {
          List<Tuple> result = queryFactory.select(team.name, member.age.avg())
                  .from(member)
                  .join(member.team, team)
                  .groupBy(team.name)
                  .fetch();
          Tuple teamA = result.get(0);
          Tuple teamB = result.get(1);
      
          assertThat(teamA.get(team.name)).isEqualTo("teamA");
          assertThat(teamA.get(member.age.avg())).isEqualTo(15);
          assertThat(teamB.get(team.name)).isEqualTo("teamB");
          assertThat(teamB.get(member.age.avg())).isEqualTo(35);
      }

 

8. 조인 - 기본 조인

  • 기본 조인
    • 조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정해두고, 두 번째 파라미터에 별칭(alias)으로 사용할 Q타입을 지정하면 된다.
      • join(조인대상, 별칭으로 사용할 Q타입)
    • join(), innerJoin() : 내부 조인 (inner join)
    • leftJoin() : left 외부 조인 (left outer join)
    • rightJoin() : right 외부 조인 (right outer join)
    • JPQL의 on과 최적화를 위한 fethch 조인 제공 → 다음 on 절 참고
      • fetch join
        • JPQL에서 성능 최적화를 위해 제공하는 조인의 종류
        • 처음 조회부터 조인시켜 데이터를 가져올 수 있게 하려고 페치 조인이 등장 (N+1문제 해결)
    • 참고 코드
      /**
       * 팀 A애 소속된 모든 회원
       */
      @Test
      public void join(){
          List<Member> result = queryFactory.selectFrom(member)
                  .join(member.team, team) // inner join
                  .where(team.name.eq("teamA")) // .on(team.name.eq("teamA")) 과 동일
                  .fetch();
      
          assertThat(result)
                  .extracting("username")
                  .containsExactly("member1", "member2"); // 순서 까지 정확해야함
      }
  • 세타 조인
    • 연관관계가 없는 필드로 조인
    • from 절에 여러 엔티티를 선택해서 세타 조인
    • 외부 조인 불가능 → 다음에 나오는 조인 on을 사용하면 외부 조인 가능
    • 참고 코드
      /**
       * 세타 조인 (연관관계가 없는 필드로 조인)
       * - cross join
       * - 선택한 엔티티의 모든 데이터를 다 조인한 후, where절로 조건 필터링
       * - from 절에 여러 엔티티를 선택해서 세타 조인.
       * - 이전엔 외부 조인 불가능 -> on을 사용하면 가능.
       * 예제
       * - 회원의 이름이 팀 이름과 같은 회원 조회
       */
      @Test
      public void theta_join(){
          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");
      }

 

9. 조인 - on절

  • ON절을 활용한 조인 (JPA 2.1부터 지원)
    1. 조인 대상 필터링
    2. 연관관계 없는 엔티티 외부 조인
  • 조인 대상 필터링
  • 예제 : 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
  • 참고 코드
    /**
     * ON절을 활용한 조인 1 : 조인 대상 필터링
     * 
     * 예제
     * - 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회 (left join)
     * - JPQL : 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'
     * 
     * 결과 예시
     * tuple = [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
     * tuple = [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]
     * tuple = [Member(id=5, username=member3, age=30), null]
     * tuple = [Member(id=6, username=member4, age=40), null]
     */
    @Test
    public void join_on_filtering() {
        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 절을 활용해 조인 대상을 필터링 할 때, 외부조인이 아니라 내부조인(inner join)을 사용하면, where 절에서 필터링 하는 것과 기능이 동일하다. 따라서 on 절을 활용한 조인 대상 필터링을 사용할 때, 내부조인 이면 익숙한 where 절로 해결하고, 정말 외부조인이 필요한 경우에만 이 기능을 사용하자. (위의 예제에서 .leftJoin(member.team, team).where(team.name.eq("teamA")) 이면 null인 케이스가 안나옴
  • 연관관계 없는 엔티티 외부 조인
    • 예제 : 회원의 이름과 팀의 이름이 같은 대상 외부 조인
    • 하이버네이트 5.1부터 on 을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능이 추가되었다. 물론 내부 조인도 가능하다.
    💡 주의!
    • 문법을 잘 봐야 한다. leftJoin() 부분에 일반 조인과 다르게 엔티티 하나만 들어간다.
      • 일반조인: leftJoin(member.team, team)
      • 연관관계 없는 on조인: from(member).leftJoin(team).on(xxx)
    • 참고 예제
      /**
       * ON절을 활용한 조인 2 : 연관관계 없는 엔티티 외부 조인
       *
       * 예제
       * - 회원의 이름과 팀의 이름이 같은 대상 외부 조인
       * - 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
       * 
       * 결과 예시
       * t=[Member(id=3, username=member1, age=10), null]
       * t=[Member(id=4, username=member2, age=20), null]
       * t=[Member(id=5, username=member3, age=30), null]
       * t=[Member(id=6, username=member4, age=40), null]
       * t=[Member(id=7, username=teamA, age=0), Team(id=1, name=teamA)]
       * t=[Member(id=8, username=teamB, age=0), Team(id=2, name=teamB)]
       */
      @Test
      public void join_on_no_relation(){
          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)) // 일반 조인과 다르게 엔티티 하나만 들어감!
                  // inner join
                  // t=[Member(id=7, username=teamA, age=0), Team(id=1, name=teamA)]
                  // t=[Member(id=8, username=teamB, age=0), Team(id=2, name=teamB)]
                  // .join(team).on(member.username.eq(team.name))
                  .fetch();
      
          for (Tuple tuple : result) {
              System.out.println("t=" + tuple);
          }
      }

 

 

10. 조인 - 페치 조인

  • 페치 조인은 SQL에서 제공하는 기능은 아니다.
  • SQL 조인을 활용해서 연관된 엔티티를 SQL 한번에 조회하는 기능이다.
  • 주로 성능 최적화에 사용하는 방법이다.
  • 사용 방법
    • join(), leftJoin() 등 조인 기능 뒤에 fetchJoin()이라고 추가하면 된다
     
참고: 페치 조인에 대한 자세한 내용은 JPA 기본편이나, 활용 2편 참고하기
  • 참고 코드
    @PersistenceUnit
    EntityManagerFactory emf;
    
    /*
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "team_id")
        private Team team;
    
    		지연로딩으로 Member, Team SQL 쿼리 각각 실행
        결과적으로 member만 가져옴
     */
    @Test
    public void fetchJoinNo(){
        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();
    }
    
    /*
    		즉시로딩으로 Member, Team SQL 쿼리 조인으로 한번에 조회
        Team을 함께 조회
        .fetchJoin() 사용
     */
    @Test
    public void fetchJoinUse() {
        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();
    }

 

11. 서브 쿼리

    • com.querydsl.jpa.JPAExpressions 사용 → static import 가능
    • from 절의 서브쿼리 한계
      • JPA JPQL 서브쿼리의 한계점으로 from 절의 서브쿼리(인라인 뷰)는 지원하지 않는다.
      • 당연히 Querydsl도 지원하지 않는다.
      • 하이버네이트 구현체를 사용하면 select 절의 서브쿼리는 지원한다.
      • Querydsl도 하이버네이트 구현체를 사용하면 select 절의 서브쿼리를 지원한다.
    • from 절의 서브쿼리 해결 방안 (1~3번 순으로 적용해보기)
      1. 서브쿼리를 join으로 변경한다. (가능한 사오항도 있고, 불가능한 상황도 있다.)
      2. 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
      3. nativeSQL을 사용한다
    • 참고 코드 - 서브쿼리 eq 사용
      /**
       * 나이가 가장 많은 회원 조회
       */
      @Test
      public void subQuery() {
      
          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);
      }
    • 참고 코드 - 서브쿼리 goe 사용
      /**
       * 나이가 평균 이상인 회원 조회
       */
      @Test
      public void subQueryGoe() {
      
          QMember memberSub = new QMember("memberSub");
      
          List<Member> result = queryFactory.selectFrom(member)
                  .where(member.age.goe(
                          JPAExpressions.select(memberSub.age.avg())
                                  .from(memberSub)
                  ))
                  .fetch();
      
          assertThat(result).extracting("age")
                  .containsExactly(30, 40);
      }

 

    • 참고 코드 - 서브쿼리 여러 건 처리 in 사용⭐
      /**
       * 나이가 10보다 많은 회원 조회
       */
      @Test
      public void subQueryIn() {
      
          QMember memberSub = new QMember("memberSub");
      
          List<Member> result = queryFactory.selectFrom(member)
                  .where(member.age.in(
                          JPAExpressions.select(memberSub.age)
                                  .from(memberSub)
                                  .where(memberSub.age.gt(10))
                  ))
                  .fetch();
      
          assertThat(result).extracting("age")
                  .containsExactly(20, 30, 40);
      }

 

  • 참고 코드 - select 절에 subquerys
    /**
     * 이름과 평균 나이
     */
    @Test
    public void selectSubQuery(){
    
        QMember memberSub = new QMember("memberSub");
    
        List<Tuple> result = queryFactory.select(member.username,
                        select(memberSub.age.avg())
                                .from(memberSub))
                .from(member)
                .fetch();
    
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

 

12. Case 문

  • 가급적이면 raw 데이터를 쿼리에서하고, 값을 변환하는 등의 일은 애플리케이션 로직에서 변경하도록 해야한다.⭐
  • select, 조건절(where), order by에서 사용 가능
  • 참고 코드
    @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);
        }
    }
    
    @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("21~30살")
                        .otherwise("기타"))
                .from(member)
                .fetch();
    
        for (String s : result) {
            System.out.println("s = " + s);
        }
    }
    
  • 참고 코드 - orderBy에서 Case문 함께 사용하기
    /**
     * orderBy에서 Case문 함께 사용하기
     * - 0~30살이 아닌 회원을 가장 먼저 출력
     * - 0~20살 회원 출력
     * - 21~30살 회원 출력
     *
     * 결과
     * username = member4 age = 40 rank = 3
     * username = member1 age = 10 rank = 2
     * username = member2 age = 20 rank = 2
     * username = member3 age = 30 rank = 1
     */
    @Test
    public void caseOrderBy(){
        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();
    
        for (Tuple tuple : result) {
            String username = tuple.get(member.username);
            Integer age = tuple.get(member.age);
            Integer rank = tuple.get(rankPath);
            System.out.println("username = " + username + " age = " + age + " rank = " + rank);
        }
    }

 

13. 상수, 문자 더하기

  • 상수
    • 상수가 필요하면 Expressions.constant(xxx) 사용
    • 참고: 최적화가 가능하면 SQL에 constant 값을 넘기지 않는다. 상수를 더하는 것 처럼 최적화가 어려우면 SQL에 constant 값을 넘긴다.
  • 문자
    • concat()을 사용한다.
    • 참고: member.age.stringValue() 부분이 중요한데, 문자가 아닌 다른 타입들은 stringValue() 로 문자로 변환할 수 있다. 이 방법은 ENUM을 처리할 때도 자주 사용한다.
  • 참고 예제
    // 상수 더하기
    @Test
    public void constant(){
        List<Tuple> result = queryFactory.select(member.username, Expressions.constant("A"))
                .from(member)
                .fetch();
    
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }
    
    // 문자 더하기 - stringValue()는 ENUM에서도 자주 사용함
    @Test
    public void concat(){
        // username_age
        List<String> result = queryFactory.select(member.username.concat("_").concat(member.age.stringValue()))
                .from(member)
                .where(member.username.eq("member1"))
                .fetch();
    
        for (String s : result) {
            System.out.println("s = " + s);
        }
    }

 

 

728x90

'Querydsl' 카테고리의 다른 글

[실전! Querydsl] 02. 예제 도메인 모델  (0) 2024.07.30
[실전! Querydsl] 01. 프로젝트 환경설정  (0) 2024.07.30