ETC

QueryDSL이란?

D_Helloper 2023. 6. 28. 22:27

QueryDSL란 ?

Query Domain Specific Language

SQL, JPQL 등을 코드로 작성할 수 있도록 해주는 빌더 오픈소스

💡 JPQL이란 ?
JPA에서 지원하는 다양한 쿼리 방법 중 가장 단순한 조회 방법으로, SQL의 경우에는 DB 테이블을 대상으로 쿼리를 질의하지만, JPQL은 엔티티 객체를 대상으로 쿼리를 질의

 

JPQL

String username = "java";
String jpql = "select m from Member m where m.username = :username";

List<Member> result = em.createQuery(query, Member.class).getResultList();

QueryDSL

String username = "java";

List<Member> result = queryFactory
        .select(member)
        .from(member)
        .where(usernameEq(username))
        .fetch();

QueryDSL의 장점

  • 문자열이 아닌 코드로 작성하기 때문에 오타 및 에러를 쉽게 찾고 방지할 수 있음
  • 동적인 쿼리 작성이 편리함
  • 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있음
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private int age;
}
import com.querydsl.core.types.dsl.*;

public class QMember extends EntityPathBase<Member> {

    public static final QMember member = new QMember("member");

    public final NumberPath<Long> id = createNumber("id", Long.class);
    public final StringPath username = createString("username");
    public final NumberPath<Integer> age = createNumber("age", Integer.class);

    // 생성자
    public QMember(String variable) {
        super(Member.class, variable);
    }
}
QMember qMember = QMember.member;

JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
List<Member> members = queryFactory.selectFrom(qMember)
        .where(qMember.age.between(20, 30))
        .fetch();

// Error: 컴파일 에러 발생
List<Member> members = queryFactory.selectFrom(qMember)
        .where(qMember.age.eq("20")) // 잘못된 타입 사용
        .fetch();
  • 위 코드에서 qMember.age가 int형인 것을 컴파일러가 알고 있기 때문에 다른 타입의 값을 활용하려고 하면 컴파일 과정에서 에러 발생 → 타입 안정성 향상
💡 QClass란 ?
QueryDSL에서 도메인 모델 또는 엔티티 클래스와 관련된 쿼리 메타 모델 생성
- Domain 클래스의 쿼리 메타 모델을 생성
- 타입 안전성을 지원
- 코드 자동 완성 기능 활용 가능
- 동적 쿼리 작성
💡 JPAQueryFactory?
QueryDSL에서 JPA와 함께 사용하기 위한 클래스,
JPQLQuery를 생성하고 관리하는 데 사용되며, 주로 JPA의 entityManager와 함께 사용

 

사용 예시

Member Entity Class

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private int age;

    // 생성자, getter, setter 등
}

QMember Class

import com.querydsl.core.types.dsl.*;

public class QMember extends EntityPathBase<Member> {

    public static final QMember member = new QMember("member");

    public final NumberPath<Long> id = createNumber("id", Long.class);
    public final StringPath username = createString("username");
    public final NumberPath<Integer> age = createNumber("age", Integer.class);

    // 생성자
    public QMember(String variable) {
        super(Member.class, variable);
    }
}

MemberRepository Interface

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom 
{
}

MemberRepositoryCustom Interface

public interface MemberRepositoryCustom {
    List<Member> findMembersByAgeBetween(int from, int to);
}

MemberRepositoryImple

import com.querydsl.jpa.impl.JPAQueryFactory;

public class MemberRepositoryImpl implements MemberRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    public MemberRepositoryImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<Member> findMembersByAgeBetween(int from, int to) {
        QMember qMember = QMember.member;

        return queryFactory.selectFrom(qMember)
            .where(qMember.age.between(from, to))
            .orderBy(qMember.age.asc())
            .fetch();
    }
}

MemberService

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public List<Member> getMembersByAgeRange(int from, int to) {
        return memberRepository.findMembersByAgeBetween(from, to);
    }
}

CRUD 사용법

// SELECT 쿼리
queryFactory.selectFrom(qMember)
            .where(qMember.age.between(20, 30))
            .orderBy(qMember.username.asc())
            .fetch();

// INSERT 쿼리
queryFactory.insert(qMember)
            .set(qMember.username, "new-username")
            .set(qMember.age, 25)
            .execute();

// UPDATE 쿼리
queryFactory.update(qMember)
            .set(qMember.username, "updated-username")
            .where(qMember.id.eq(1L))
            .execute();

// DELETE 쿼리
queryFactory.delete(qMember)
            .where(qMember.id.eq(1L))
            .execute();