Querydsl - (2)

bp.chys·2020년 6월 15일
1

JPA

목록 보기
12/15

검색 조건 쿼리

@Test
public void search() {
    Member findMember = queryFactory
            .selectFrom(member)
            .where(member.username.eq("member1")
                .and(member.age.eq(10)))
            .fetchOne();
            
    assertThat(findMember.getUsername()).isEqualTo("member1");
}
  • 검색 조건은 .and(), .or()를 메서드 체인으로 연결할 수 있다.
  • 이외에도 .eq, .ne, .isNotNull, .in, .notIn, .between, .goe, .loe, .gt, .lt, .like, .contains, .startsWith 등이 있다.

and 조건을 파라미터로 처리

@Test
public void search() {
    Member findMember = queryFactory
            .selectFrom(member)
            .where(
                member.username.eq("member1"),
                member.age.eq(10))
            .fetchOne();
            
    assertThat(findMember.getUsername()).isEqualTo("member1");
}
  • 이렇게 하면 조건이 null인 경우 무시할 수 있기 때문에, 추후 메서드 추출을 활용해서 동적쿼리를 깔끔하게 만들 수 있다.

결과 조회

  • fetch() : 리스트 조회, 데이터가 없으면 빈 리스트 반환
  • fetchOne() : 단 건 조회, 결과가 없으면, null을 반환하고 둘 이상이면 예외를 발생시킨다.
  • fetchFirst() : limit(1).fetchOne()
  • fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행한다.
  • fetchCount() : count쿼리로 변경해서 count 수를 조회한다.

정렬

@Test
public void sort() {
    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(member5.getUsername()).isEqualTo("member6");
    assertThat(member5.getUsername()).isNull();    
}
  • asc(), desc() : 일반 정렬
  • nullsLast(), nullsFirst() : null 데이터 순서 부여

페이징

@Test
public void paging1() {
    List<Member> result = queryFactory
            .selectFrom(member)
            .orderBy(member.username.desc())
            .offset(1) // 0부터 시작
            .limit(2)  // 최대 2건 조회
            .fetch();
            
    assertThat(result.size()).isEqualTo(2);
}
  • 전체 조회수가 필요하다면, fetch 대신 fetchResults를 사용한다.
  • 이때,count 쿼리가 실행되니 join을 한다면 성능상 주의를 해야한다.

집합

@Test
public void aggregation() throws Exception {

    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);
}
  • tuple을 반환한다.

조인

기본 조인

  • 조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 별칭으로 사용할 Q타입을 지정하면 된다.
join(조인 대상, 별칭으로 사용할 Q타입)
@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() : 내부 조인
  • leftJoin() : left 외부 조인
  • rightJoin() : right 외부 조인
  • JPQL의 on과 성능 최적화를 위한 fetch Join 제공

세타 조인

  • 연관관계가 없는 필드로 조인
  • 회원의 이름이 팀 이름과 같은 회원 조회
@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절 활용 - (1) 조인 대상 필터링

@Test
public void join_on_filtering() throws Exception {   
    List<Member> 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절에서 필터링하는 것과 동일하기 때문에 내부조인이라면, where절로 하고, 외부 조인일 경우만 on을 활용하자.

on절 활용 - (2) 연관관계 없는 엔터티 외부 조인

@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);
    } 
}
  • leftJoin() 부분에 일반조인과 다르게 엔티티 하나만 들어간다.

페치 조인

@Test
public void fetchJoinUse() throws Exception { 

    em.flush();
    em.cleer();

    Member findMember = queryFactory 
            .selectFrom(member) 
            .join(member.team, team).fetchJoin()
            .where(member.username.eq("member1"))
            .fetchOne();
            
    boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getItem());
    assertThat(loaded).as("페치 조인 적용").isTrue();
}
  • 사용 방법은 간단하다. join(), leftJoin() 뒤에 fetchJoin()이라고 추가하면 된다.

서브쿼리

com.querydsl,jpa,JPAExpressions 사용

where절 - 서브 쿼리 eq사용

...
List<Member> result = queryFactory
        .selectFrom(member)
        .where(member.age.eq(
                JPAExpressions
                    .select(memberSub.age.max())
                    .from(memberSub)
        ))
        .fetch();
assertThat(result).extracting("age")
        .containsExtactly(40);
}

select절

List<Tuple> fetch = queryFactory
        .select(member.username,
                JPAExpressions  // static import 가능
                        .select(memberSub.age.avg())
                        .from(memberSub)
        ).from(member)
        .fetch();
        
for (Tuple tuple : fetch) {
    System.out.println("username = " + tuple.get(member.username));
    System.out.println("age = " + 
            tuple.get(JPAExpressions.select(memberSub.age.avg()) .from(memberSub)));
}
  • JPA JPQL 서브 쿼리의 한계점으로 from 절의 서브쿼리는 지원하지 않는다. 당연히 querydsl도 지원하지 않는다.
profile
하루에 한걸음씩, 꾸준히

0개의 댓글