Notice
Recent Posts
Recent Comments
Link
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Tags
more
Archives
Today
Total
관리 메뉴

채채

QueryDSL이란? 본문

spring boot

QueryDSL이란?

HChaeEun 2023. 12. 7. 12:23

자바로 백엔드를 개발할 때 주로 Spring Boot와 Spring Data JPA를 같이 사용한다.

그러나 JPA를 사용하다보면 N+1문제를 직면하기도 하며, 이를 해결하기 위해 다양한 조인 조건과 Fetch Join 등의 복잡한 쿼리를 작성할 때 JPA로 작성하기엔 메소드명이 길어짐에 따라 가독성이 떨어지게 된다. 또한 동적쿼리를 작성하는 것에 있어서 JPA는 제약이 존재한다.

post findAll을 했을 때 발생하는 N+1 문제
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
// age가 주어진 값보다 크고, city가 주어진 문자열을 포함하며, name이 주어진 접두사로 시작하는 엔터티들을 찾는다.
    List<MyEntity> findByAgeGreaterThanAndCityContainingAndNameStartingWith(int age, String city, String namePrefix);
}

(조건이 많아질수록 이렇게 길게 작성되는게 개인적으로 너무너무 싫다...)

 

그래서 나타난 것이 MyBatis, JPQL, Criteria 등 문자열 형태로 쿼리문을 작성하는 것인데, 이것의 문제점은 컴파일 시 오류를 발견할 수 없다는 것이다.

// JPA를 JPQL로 바꾸면
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
    @Query("SELECT e FROM MyEntity e " +
           "WHERE e.age > :age " +
           "AND e.city LIKE %:city% " +
           "AND e.name LIKE :namePrefix%")
    List<MyEntity> findByAgeCityAndName(@Param("age") int age, @Param("city") String city, @Param("namePrefix") String namePrefix);
}

QueryDSL의 등장

QueryDSL은 문자열이나 XML 파일에 쿼리를 작성하는 대신, 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 하는 프레임워크다. 

따라서 IDE로 코드 자동 완성 기능 사용 및 문법적으로 잘못된 코드를 발견 할 수 있는 장점이 있으며, 도메인 타입과 프로퍼티를 안전하게 참조할 수 있다. 문자열이 아니라 코드로 쿼리를 작성하면서 컴파일 시점에 문법 오류를 확인하는 것도 가능하다. 또한 동적 쿼리나 복잡한 쿼리를 쉽게 구현할 수 있게 되었다.

 

장점 요약

  • IDE의 코드 자동 완성 기능 사용
  • 문법적으로 잘못된 쿼리를 컴파일 시점에 확인 가능
  • 복잡한 쿼리나 동적 쿼리 쉽게 구현
  • 도메인 타입과 프로퍼티를 안전하게 참조 

 

cf) 동적 쿼리란?

먼저 정적 쿼리(Static SQL)은 파라미터에 의해 특정 조건이나 상황이 변하지 않는 쿼리이다.

반면 동적 쿼리(Dynamic SQL)은 파라미터 값에따라 다른 조건으로 조회하는 쿼리이다.

 

예를 들어 조회 버튼을 클릭했을 때 회원 ID가 1인 데이터를 조회하도록 정해져있다면 쿼리는 다음과 같을 것이다.

SELECT *
FROM USER
WHERE userID = 1

 

 

어떠한 조건이나 상황에따라 변하지 않고 무조건 회원 ID가 1인 데이터를 조회 하도록 되어있으므로 이는 정적 쿼리이다.

 

그런데 조회 버튼 옆에 체크박스가 생겨서 체크박스가 선택된 경우에는 회원ID가 1인 데이터를 조회하고 체크되어있지 않으면 회원ID가 0인 데이터를 조회하도록 정해졌다면

SELECT *
FROM USER
WHERE if(checked == true) {
userID = 1
}
if (checked == false) {
userID = 0
}

checked 변수에따라 회원ID가 1인 데이터이거나 0인 데이터를 조회하게 되며, 이것을 동적 쿼리라고 한다.

 

DSL(Domain Specific Languages)란?

특정 도메인에서 발생하는 문제를 효과적으로 해결하기 위해 설계된 언어를 의미한다.

예를들어 데이터베이스 내의 정보를 CRUD하기 위한 SQL

웹의 디자인, 레이아웃 및 시각적 스타일링을 정의하기 위한 CSS

문자열 내에서 특정 패턴을 검색, 추출, 교체하기 위한 Regex

등이 이에 해당한다.

 

다시 QueryDSL을 정의하자면.

SQL 형식의 쿼리Type-Safe 하게 생성할 수 있도록 하는 DSL을 제공하는 라이브러리다.

 

실제로 QueryDSL은 다양한 모듈을 제공하지만, 이 시간 알아볼 것은 QueryDSL JPA를 의미한다.

 

QueryDSL 설정 방법

1. querydsl-jpa 의존성 추가

springboot 3로 올라오면서 javax에서 jakarta로 변경되었으므로, querydsl-jpa 버전 명시 뒤에 :jakarta 추가를 꼭 해야한다.

dependencies{
	...
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    ...
}

 

2. QClass 생성을 위한 annotationProcessor 추가

dependencies{
	...
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    ...
}

 

- QClass: 엔티티 클래스 속성과 구조를 설명해주는 메타데이터로 Type-safe 하게 쿼리 조건 설정을 가능하게 한다.

// Member.java
@Entity
public class Member {
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private int age;
    @ManyToOne(fetch = FetchType.LAZY)
    private Team team;
    
    ...
}

이러한 entity가 있다고 했을 때, annotationProcessor를 통해 생성된 QClass는 QMember이다.

// QMember.java
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QMember extends EntityPathBase<Member> {

	private static final long serialVersionUID = 324647761L;
    
    private static final PathInits INITS = PathInits.DIRECT2;
    
    public static final QMember member = new QMember("member1");
    
    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 final QTeam team;
    
    public QMember(String variable) {
    	this(Member.class, forVariable(variable), INITS);
    }
    
    ...
}

 

 

참고:

http://querydsl.com/static/querydsl/3.5.1/reference/ko-KR/html_single/

 

Querydsl - 레퍼런스 문서

본 절에서는 SQL 모듈의 쿼라 타입 생성과 쿼리 기능을 설명한다. com.mysema.query.sql.Configuration 클래스를 이용해서 설정하며, Configuration 클래스는 생성자 인자로 Querydsl SQL Dialect를 취한다. 예를 들어

querydsl.com

https://www.inflearn.com/questions/743461/%EC%A0%95%EC%A0%81%EC%BF%BC%EB%A6%AC%EC%99%80-%EB%8F%99%EC%A0%81%EC%BF%BC%EB%A6%AC%EC%9D%98-%EA%B0%9C%EB%85%90%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EA%B6%81%EA%B8%88%ED%95%A9%EB%8B%88%EB%8B%A4

 

정적쿼리와 동적쿼리의 개념의 차이점이 궁금합니다. - 인프런 | 질문 & 답변

Querydsl 기술은 복잡한 쿼리를 작성할 때나 동적쿼리를 쉽게 작성할 때 큰 강점을 가진 것으로 알고 있습니다. 그런데 정적쿼리와 동적쿼리의 개념에 대한 차이점이 잘 와 닿지가 않아 질문을 드

www.inflearn.com

https://ittrue.tistory.com/292

 

[JPA] QueryDSL 소개 및 장점

QueryDSL이란? QueryDSL은 하이버네이트 쿼리 언어(HQL: Hibernate Query Language)의 쿼리를 타입에 안전하게 생성 및 관리해주는 프레임워크이다. QueryDSL은 정적 타입을 이용하여 SQL과 같은 쿼리를 생성할

ittrue.tistory.com

https://hhhhhhhong.tistory.com/88

 

QueryDSL은 왜 Q-Class를 사용할까?

QueryDSl로 개발을 하려면 Q-Class를 이용해 개발을 해야 한다. Q-Class와 같은걸 만들어 사용하는 방식이 처음이라 그런지 왜 굳이 이런 방식으로 사용하는거지? 라는 궁금증이 생기게 되었다. 그래서

hhhhhhhong.tistory.com