Spring/Spring Data JPA

[자바 ORM 표준 JPA 프로그래밍] 10-1. 객체지향 쿼리 언어: JPQL

noahkim_ 2025. 4. 24. 10:34

김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.

 

1. JPQL

  • 객체 지향 쿼리
  • SQL 추상화
  • 실행 시점에 SQL 문을 생성하여 요청함

 

예제) JPQL ➡️ SQL

더보기
// 쿼리 생성
String jpql = "select m from Member as m where m.username = 'kim'";
List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();

 

특징

구분 설명
기본 문법
SQL과 유사한 문법 사용 (SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY 등)
- 엔티티명과 필드 사용, 별칭 필수
쿼리 객체 API
- TypedQuery: 반환 타입 명확할 때
- Query: 반환 타입 명확하지 않을 때
결과 조회
- getResultList() : 결과 리스트 반환
- getSingleResult() : 단일 결과, 예외 발생 가능성 있음
파라미터 바인딩 - 이름 기반 :name
- 위치 기반 ?1, ?2
프로젝션
SELECT 절에 조회할 대상을 지정하는 것
- 엔티티, 임베디드, 스칼라 타입 선택 가능
- ex) DTO 매핑
페이징 API
setFirstResult(int), setMaxResults(int) 사용
집합 함수
COUNT, SUM, AVG, MAX, MIN 지원. NULL은 무시됨
GROUP BY / HAVING
그룹화 및 조건 필터링
ORDER BY
정렬 시 사용. AS 별칭 지정하여 정렬 가능
조인 - 내부 조인 (LAZY)
- 외부 조인 (LAZY)
- 컬렉션 조인 (LAZY)
- 페치 조인 (FETCH)

 

예제) 기본 쿼리

더보기
@Test
@Transactional
void select() {
    String jpql = "select u from User as u where u.name = 'updated'";
    User result = entityManager.createQuery(jpql, User.class).getSingleResult();

    assertEquals(result.getName(), "updated");
    assertEquals(result.getId(), 1);
}

 

예제) 쿼리 객체 API

더보기
@Test
@Transactional
void typedQuery() {
    String jpql = "select u from User as u where u.name = 'updated'";
    TypedQuery<User> query = entityManager.createQuery(jpql, User.class);

    User result = query.getSingleResult();

    assertEquals(result.getName(), "updated");
    assertEquals(result.getId(), 1);
}
@Test
@Transactional
void query() {
    String jpql = "select u from User as u where u.name = 'updated'";
    Query query = entityManager.createQuery(jpql, User.class);

    List result = query.getResultList();

    assertTrue(!result.isEmpty());
}

 

예제) 결과 조회 + 파라미터 바인딩

더보기
List<Member> members = em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class)
                         .setParameter("username", username)
                         .getResultList();
                         
List<Member> members = em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class)
                         .setParameter(1, username)
                         .getResultList();

 

예제) 프로젝션

더보기
@Test
@Transactional
void projection_scala() {
    String jpql = "select u from User as u where u.name = 'updated'";
    Query query = entityManager.createQuery(jpql, User.class);

    List result = query.getResultList();

    assertTrue(!result.isEmpty());
}
@Test
@Transactional
void projection_dto() {
    String jpql = "select new jpql.UserDto(u.id, u.name) from User as u where u.name = 'updated'";
    TypedQuery<UserDto> query = entityManager.createQuery(jpql, UserDto.class);

    UserDto result = query.getSingleResult();

    assertEquals(result.getName(), "updated");
    assertEquals(result.getId(), 1);
}

 

예제) 페이징 API

더보기
@Test
@Transactional
void paging() {
    String jpql = "select u from User as u order by u.id DESC";
    TypedQuery<User> query = entityManager.createQuery(jpql, User.class);

    query.setFirstResult(2); // offset
    query.setMaxResults(10); // 결과 셋 최대 크기

    List<User> result = query.getResultList();
}

 

예제) 집계 함수

더보기
@Test
@Transactional
void aggregation() {
    String jpql = "select count(u), max(u.name) from User as u";
    TypedQuery<Object[]> query = entityManager.createQuery(jpql, Object[].class);

    List<Object[]> result = query.getResultList();
    Object[] row = result.get(0);

    System.out.println("count: " + row[0]);
    System.out.println("max: " + row[1]);
}

 

예제) GROUP BY / HAVING

더보기
@Test
@Transactional
void grouping() {
    String jpql = "select u.name, count(u.id) from User as u group by u.name";
    TypedQuery<Object[]> query = entityManager.createQuery(jpql, Object[].class);

    List<Object[]> result = query.getResultList();
    for (Object[] row : result) {
        System.out.println("count: " + row[0]);
        System.out.println("max: " + row[1]);
        System.out.println();
    }
}

 

예제) 조인

더보기

내부 조인

@Test
@Transactional
void join() {
    String jpql = "select u from User u INNER JOIN u.team t";
    TypedQuery<User> query = entityManager.createQuery(jpql, User.class);

    List<User> result = query.getResultList();
    for (User row : result) {
        System.out.println("id: " + row.getId());
        System.out.println("name: " + row.getName());
        System.out.println("team id: " + row.getTeam().getName());
        System.out.println();
    }
}

 

컬렉션 조인

@Test
@Transactional
void collection_join() {
    String jpql = "select u from Team t JOIN t.members u WHERE t.name = :teamName";
    TypedQuery<User> query = entityManager.createQuery(jpql, User.class)
            .setParameter("teamName", "gsw");

    List<User> result = query.getResultList();
    for (User row : result) {
        System.out.println("id: " + row.getId());
        System.out.println("name: " + row.getName());
        System.out.println("team id: " + row.getTeam().getName());

        System.out.println();
    }
}

 

페치 조인

@Test
@Transactional
void fetch_join() {
    String jpql = "select u from User u JOIN FETCH u.team t";
    TypedQuery<User> query = entityManager.createQuery(jpql, User.class);

    List<User> result = query.getResultList();
    for (User row : result) {
        System.out.println("id: " + row.getId());
        System.out.println("name: " + row.getName());
        System.out.println("team id: " + row.getTeam().getName());

        System.out.println();
    }
}