JPA (11) JPQL문법, Projection, Pagination

김영한님의 자바 ORM 표준 JPA 프로그래밍 강의 정리

JPQL 문법

  • select m from Member as m where m.age > 18
  • 엔티티와 속성은 대소문자 구분
  • JPQL 키워드는 대소문자 구문하지 않음
  • 테이블 이름이 아닌 엔티티 이름을 사용(@Entity(name = "member"))
  • 별칭 필수(as 생략 가능)
  • 집계 함수 가능

    select COUNT(m), SUM(m.age), AVG(m.age), MAX(m.age), MIN(m.age) from Member m
    
  • 집합 & 정렬
    • GROUP BY, HAVING
    • ORDER BY
  • TypeQuery
    • 반환 타입이 명확할 때 사용
    TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
    
  • Query
    • 반환 타입이 명확하지 않을 때 사용
    Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
    
  • 결과 조회 API
    • query.getResultList()
      • 결과가 하나 이상일 때
      • 리스트를 반환하고 결과가 없으면 빈 리스트 반환
      • NullPointerException에 안전
    • query.getSignleResult()
      • 결과가 하나일 때
      • 단일 객체 반환
      • 결과가 없으면 javax.persistence.NoResultException
      • 결과가 둘 이상이면 java.persistence.NonUniqueResultException
  • 파라미터 바인딩
    • 이름 기준
    em.createQuery("select m from Member m where m.username = :username", Member.class)
                        .setParameter("username", "John")
                        .getSingleResult();
    
    • 위치 기준
    em.createQuery("select m from Member m where m.username = ?username", Member.class)
                        .setParameter(1, "John")
                        .getSingleResult();
    
    • 위치 기준은 파라미터가 새로 추가될 경우 에러가 발생할 가능성이 크므로 이름 기준으로 바인딩

Projection

  • SELECT 절에 조회할 대상을 지정하는 것
  • 대상
    • Entity
    • Embedded type
    • Scalar type(숫자, 문자 등 기본 데이터 타입)
    SELECT m FROM Member m	// 엔티티 프로젝션
    SELECT m.team FROM Member m	// 엔티티 프로젝션
    SELECT m.address FROM Member m	// 임베디드 타입 프로젝션
    SELECT m.username, m.age FROM Member m	// 스칼라 타입 프로젝션
    
  • DISTINCT로 중복 제거

  • JOIN
    • SQL과 비슷하게 JPQL을 짜는 것이 좋음
    // 실제 쿼리는 inner join으로 team 데이터를 가져옴
    em.createQuery("select m.team from Member m", Team.class).getResultList();
      
    // 실제 쿼리와 맞춰서 join으로 jpql을 짜는 것이 좋음
    em.createQuery("select t from Member m join m.team t", Team.class).getResultList();
    
  • 프로젝션을 하면 대상이 모두 영속성 컨텍스트에 의해 관리됨
  • 여러 값 조회
    • Query 타입으로 조회
    List list = em.createQuery("select m.username, m.age from Member m")
                        .getResultList();
    Object o = list.get(0);
    Object[] result = (Object[]) o;
    System.out.println("username: " + result[0] + " age: " + result[1]);
    
    • Object[] 타입으로 조회
    List<Object[]> list = em.createQuery("select m.username, m.age from Member m")
                        .getResultList();
    Object[] result = list.get(0);
    System.out.println("username: " + result[0] + " age: " + result[1]);
    
    • new 명령어로 단순 값을 DTO로 조회
    • 패키지 명을 포함한 전체 클래스 명을 입력하며 순서와 타입이 일치하는 생성자 필요
    // MemberDTO
    package entity;
      
    public class MemberDTO {
        private String username;
        private int age;
      
        public MemberDTO(String username, int age) {
            this.username = username;
            this.age = age;
        }
      
        public String getUsername() {
            return username;
        }
      
        public void setUsername(String username) {
            this.username = username;
        }
      
        public int getAge() {
            return age;
        }
      
        public void setAge(int age) {
            this.age = age;
        }
    }
      
    // main
    List<MemberDTO> list = em.createQuery("select new entity.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
                        .getResultList();
    System.out.println("username: " + list.get(0).getUsername());
    System.out.println("age: " + list.get(0).getAge());
    

Pagination

  • 2개의 API로 추상화
    • setFirstResult(int startPsotion): 조회 시작 위치(0부터 시작)
    • setMaxResults(int maxResult): 조회할 데이터 수
  • 각 DB마다 맞는 쿼리가 실행됨(ex. Oracle의 rownum)
em.createQuery("select m from Member m order by m.age desc", Member.class)
                    .setFirstResult(1)
                    .setMaxResults(5)
                    .getResultList();
-- MySQL
SELECT m.id as id, m.age as age, m.team_id as team_id, m.name as name
FROM member m ORDER BY m.age DESC LIMIT ?, ?