본문 바로가기
Backend/Spring

[Sping/Java] JPQL vs QueryDSL

by persi0815 2024. 7. 7.

https://good-mood.medium.com/spring-data-jpa-querydsl-taking-best-from-both-worlds-2bd80af9d0a9

 

JPQL(Java Persistence Query Language)과 QueryDSL은 둘 다 Java 애플리케이션에서 데이터베이스 쿼리를 작성하는 데 사용되지만, 접근 방식과 사용 용도에서 차이가 있다.
 

1. JPQL (Java Persistence Query Language)

 

JPA의 일부로, 쿼리를 테이블이 아닌 엔티티 객체를 대상으로 작성하는 정적인 객체지향 쿼리 언어이다. 

 

JPQL은 SQL과 유사한 문법을 사용하지만, 데이터베이스 테이블 대신 Java 엔티티 객체를 다루며, 필드나 속성을 통해 데이터베이스 레코드를 조회하고 조작할 수 있다. 이를 통해 데이터베이스와의 상호작용을 객체지향적으로 처리할 수 있게 해준다.

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
    private BoardType boardType;
    // getters and setters
}

public interface BoardRepository extends JpaRepository<Board, Long> {
    @Query("SELECT b FROM Board b WHERE b.boardType = :boardType")
    List<Board> findByBoardType(@Param("boardType") BoardType boardType);
}

 

 

JPQL 문제점

1. 정적 쿼리만 작성할 수 있어, 런타임에 동적으로 변경하기 어렵다.

    → 복잡한 조건부 쿼리를 작성할 때는 코드가 복잡해지고 유지보수가 어려워질 수 있다.

 

2. 문자열 기반으로 쿼리를 작성하기에 컴파일 타임에 쿼리 문법 오류나 타입 오류를 확인할 수 없다. 

    → 런타임에 오류를 발견하여 디버깅과 오류 수정에 더 많은 시간을 소모하게 된다. 

    → 반면, QueryDSL은 자바 코드 기반으로 쿼리를 작성하므로, 컴파일 타임에 오류를 발견할 수 있어 더 안전하다.

 

3. 문자열로 쿼리 작성하기에 IDE 지원이 제한적이다. 

    → 개발 생산성에 부정적인 영향을 미칠 수 있다. 

    → QueryDSL은 자바 코드로 쿼리를 작성하므로, IDE의 자동 완성 기능을 최대한 활용할 수 있다. 

 

4. 문자열로 쿼리 작성하기에 유지보수가 어렵다. 

    → 쿼리 수정하거나 조건 추가할 때 코드의 가독성과 유지보수성이 떨어진다. 

    → QueryDSL은 자바 코드로 쿼리를 작성하므로, 코드의 가독성과 유지보수성이 훨씬 좋다.

 

2. QueryDSL

쿼리를 테이블이 아닌 엔티티 객체를 대상으로 작성하는 동적인 객체지향 쿼리 언어이다.

 

QueryDSL은 자바 코드 기반의 DSL(Domain Specific Language)을 사용하여 쿼리를 작성하며, 런타임에 쿼리 조건을 유연하게 추가하거나 변경할 수 있다. 이를 통해 데이터베이스와의 상호작용을 객체지향적으로 처리할 수 있으며, 타입 안전성과 IDE 지원을 통해 개발 생산성을 높일 수 있다.

@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
    private BoardType boardType;
    private int likeCount;
    private LocalDateTime createdAt;
    // getters and setters
}

public interface BoardRepositoryCustom {
    List<Board> findByBoardTypeAndWay(BoardType boardType, String way, Pageable pageable);
}

public class BoardRepositoryImpl implements BoardRepositoryCustom {
    
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<Board> findByBoardTypeAndWay(BoardType boardType, String way, Pageable pageable) {
        JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
        QBoard board = QBoard.board;

        BooleanExpression predicate = board.boardType.eq(boardType);
        if (boardType == BoardType.ALL) {
            predicate = board.isNotNull(); // 모든 게시판 타입을 선택
        }

        JPAQuery<Board> query = queryFactory.selectFrom(board)
                .where(predicate)
                .orderBy(way.equals("like") ? board.likeCount.desc() : board.createdAt.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize());

        return query.fetch();
    }
}

 

위 예시에서 Board 엔티티를 대상으로 QueryDSL 쿼리를 작성하였다. JPAQueryFactory를 사용하여 동적 쿼리를 생성하고, 조건에 따라 정렬 기준을 변경할 수 있다. 이는 SQL이 아닌 엔티티 객체 Board를 대상으로 하며, 런타임에 조건을 유연하게 변경할 수 있다.

 

이와 같이 QueryDSL은 쿼리를 테이블이 아닌 엔티티 객체를 대상으로 작성하는 동적인 객체지향 쿼리 언어이다. 이를 통해 데이터베이스와의 상호작용을 보다 객체지향적으로 처리할 수 있으며, 타입 안전성과 동적 쿼리 작성을 지원하여 개발 생산성을 높일 수 있다.

 

3. 선택기준

 

정적 쿼리가 많고 비교적 간단한 쿼리를 사용하는 경우에는 JPQL이 적합하다.

동적 쿼리가 많고 복잡한 조건을 다루어야 하는 경우에는 QueryDSL이 더 적합하다.