-> 블로그 이전

[QueryDSL] 기본적인 조회

2022. 8. 26. 19:55Language`/JPA

JPAQueryFactory

기본적으로 QueryDSL을 통해서 query를 생성하기 위해서는 "JPAQueryFactory"로부터 쿼리를 생성해야 한다.

하지만 JPAQueryFactory가 필요한 모든 곳에서 JPAQueryFactory를 가져오는 것은 큰 낭비이고 JPAQueryFactory를 사용하기 위해서는 "EntityManager"도 필요하기 때문에 불편하다

>> JPAQueryFactory를 @Bean을 통해서 빈으로 등록해버리면 프로젝트 전역에서 편리하게 JPAQueryFactory를 사용할 수 있게 된다

@Configuration
public class QueryDSLConfig {
    @PersistenceContext
    private EntityManager em;

    @Bean
    JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

 

쿼리 타입 (Q)

QueryDSL을 사용할 때 각 빌드 방법(Gradle / IntelliJ IDEA)에 맞게 빌드를 하게되면 지정된 경로로 "쿼리 타입(Q)"가 각 Entity별로 생성된다

왼쪽 = Member / 오른쪽 = Member에 대한 쿼리 타입 QMember

왼쪽 Member Entity에 대해서 빌드를 하고난 후 쿼리 타입 QMember가 생성되었다

Member Entity의 모든 필드는 QMember에서 프로퍼티 접근을 통해서 접근할 수 있게 생성되었다

QMember member1 = QMember.member; // member1
QMember member2 = new QMember("m"); // m

기본적으로 QMember은 위와 같이 사용하는데 만약 static으로 QMember에 대해서 member를 가져오게 되면 해당 member에 대한 Alias는 자동적으로 "member1"이 된다

반면에 new를 통해서 QMember를 생성하고 내부에 variable을 지정하게 된다면 해당 variable이 Member에 대한 Alias가 된다

  • 이 두가지 경우중에서 static으로 QMember에 대해서 member를 가져오면 import static을 활용해서 인스턴스를 더 편리하게 사용할 수 있다
import static JPA.QueryPractice.domain.QMember.member;
import static JPA.QueryPractice.domain.QOrder.order;
import static JPA.QueryPractice.domain.QProduct.product;
import static JPA.QueryPractice.domain.QTeam.team;

 

select - from

// selectFrom
List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.goe(25))
        .fetch();

// select(Entity) - from(Entity)
List<Member> fetch2 = query.select(member)
        .from(member)
        .where(member.age.goe(25))
        .fetch();
        
// select(필드) - from(Entity)
List<Tuple> fetch3 = query.select(member.id, member.username, member.age)
        .from(member)
        .where(member.age.goe(25))
        .fetch();

selectFrom은 From에서 조회하려는 엔티티 전체를 조회할 때 사용하는 메소드이다

반면 select - from은 From에서 조회하려는 엔티티 전체중에서 [전체 or 일부분]을 조회할 때 사용하는 메소드이다

1. selectFrom / 2. select(Entity) - from(Entity) / 3. select(필드) - from(Entity)

 

그러면 fetch3에서 조회한 [id, username, age]가 아닌 enabled라는 필드에 대해서 Tuple에서 꺼내서 조회하면 어떻게 될까?

List<Tuple> fetch3 = query.select(member.id, member.username, member.age)
        .from(member)
        .where(member.age.goe(25))
        .fetch();

for (Tuple tuple : fetch3) {
    System.out.println("Enabled = " + tuple.get(member.enabled));
}

결과로 알 수 있듯이 Member Table상에서 enabled의 값에는 전부 true/false로 들어가있지만 query상에서 enabled를 조회하지 않았기 때문에 전부 null로 들어가게 된다

 

where

QueryDSL의 where절에는 and/or도 사용할 수 있고 "BooleanExpression"을 통해서 동적 쿼리도 굉장히 편리하게 작성할 수 있다

  • 동적 쿼리와 관련된 BooleanExpression은 추후 포스팅
List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.goe(25).and(member.username.contains("nus1")))
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.age.lt(25).and(member.username.like("%nus1%")))
        .fetch();

where절에는 필드간 비교/매핑/체이닝을 위한 메소드는 굉장히 많다

 

and(Predicate), or(Predicate)

where절에 여러 조건들을 chaining해서 판별할 때 and()/or()을 사용한다

List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.goe(25).and(member.username.contains("nus1")))
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.age.goe(25).or(member.username.contains("nus1")))
        .fetch();

 

andAnyOf(Predicate....), orAllOf(Predicate...)

andAnyOf(..)는 [앞의 조건 AND {Predicate1 OR Predicate2 OR Predicate3 OR ....}]의 query가 나가게 된다

orAllOf(..)는 [앞의 조건 OR {Predicate1 AND Predicate2 AND Predicate3 AND ...}]의 query가 나가게 된다

List<Member> fetch1 = query.selectFrom(member)
        .where(member.username.contains("nus1").andAnyOf(member.age.goe(25), member.enabled.isTrue()))
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.username.contains("nus1").orAllOf(member.age.lt(25), member.enabled.isFalse()))
        .fetch();

 

isTrue(), isFalse()

isTrue()/isFalse()는 Boolean 필드에 대해서 true/false를 찾을 때 사용한다

not()의 경우 "앞의 필드 값이 아닌 것"에 대해서 query를 날린다

List<Member> fetch1 = query.selectFrom(member)
        .where(member.enabled.isTrue())
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.enabled.isFalse())
        .fetch();

List<Member> fetch3 = query.selectFrom(member)
        .where(member.age.eq(25).not()) // 멤버 나이가 25살이 "아닌"
        .fetch();

 

eq(값), not()

eq()의 경우 "앞의 필드와 인자의 값이 동일한 것"에 대해서 query를 날린다

not()의 경우 "앞의 필드 값이 아닌 것"에 대해서 query를 날린다

List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.eq(25)) // 멤버 나이가 25살
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.age.eq(25).not()) // 멤버 나이가 25살이 "아닌"
        .fetch();

 

gt(값), lt(값), goe(값), loe(값)

gt = 왼쪽 필드가 "값"보다 큰 것에 대한 query

lt = 왼쪽 필드가 "값"보다 작은 것에 대한 query

goe = 왼쪽 필드가 "값"보다 크거나 같은 것에 대한 query

loe = 왼쪽 필드가 "값"보다 작거나 같은 것에 대한 query

List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.gt(25)) // age > 25
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.age.lt(25)) // age < 25
        .fetch();

List<Member> fetch3 = query.selectFrom(member)
        .where(member.age.goe(25)) // age >= 25
        .fetch();

List<Member> fetch4 = query.selectFrom(member)
        .where(member.age.loe(25)) // age <= 25
        .fetch();

왼쪽 = gt / 오른쪽 = lt
왼쪽 = goe / 오른쪽 = loe

 

between(from, to), notBetween(from, to)

[from, to]범위에 포함된 값/[from, to]범위에 포함되지 않은 값에 대한 query를 날리게 된다

List<Order> fetch1 = query.selectFrom(order)
        .where(order.orderDate.between(
                LocalDateTime.of(2022, 8, 1, 0, 0, 0),
                LocalDateTime.of(2022, 8, 10, 0, 0, 0)
        )).fetch();

List<Order> fetch2 = query.selectFrom(order)
        .where(order.orderDate.notBetween(
                LocalDateTime.of(2022, 8, 1, 0, 0, 0),
                LocalDateTime.of(2022, 8, 10, 0, 0, 0)
        )).fetch();

between
notBetween

 

like(문자열), notLike(문자열), startsWith(문자열), endsWith(문자열), contains(문자열)

List<Member> fetch1 = query.selectFrom(member)
        .where(member.username.like("%nus1")) // username LIKE '%nus1%'
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.username.notLike("%nus1")) // username NOT LIKE '%nus1%'
        .fetch();

List<Member> fetch3 = query.selectFrom(member)
        .where(member.username.startsWith("nus1")) // username LIKE 'nus1%'
        .fetch();

List<Member> fetch4 = query.selectFrom(member)
        .where(member.username.endsWith("nus1")) // username LIKE '%nus1'
        .fetch();

List<Member> fetch5 = query.selectFrom(member)
        .where(member.username.contains("nus1")) // username LIKE '%nus1%'
        .fetch();

왼쪽 = like / 오른쪽 = notLike
왼쪽 = startsWith / 가운데 = endsWith / 오른쪽 = contains

 

in(값...), notIn(값...)

// List
List<Integer> list = new ArrayList<>();
list.add(20); list.add(25); list.add(30);

// Query
List<Member> fetch1 = query.selectFrom(member)
        .where(member.age.in(list))
        .fetch();

List<Member> fetch2 = query.selectFrom(member)
        .where(member.age.notIn(list))
        .fetch();