ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 05. JPA 객체지향 쿼리 언어(JPQL)
    BackEnd/JPA 2021. 8. 13. 17:03
    반응형

    1. JPQL(Java Persistence Query Language)이란?

      > JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공한다. SQL은 데이터베이스 테이블을 대상으로 쿼리하지만, JPQL은 엔티티 객체를 대상으로 쿼리하며 SQL과 문법 유사하다. (SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원)

     

    2. JPQL 문법

      > 엔티티와 속성은 대소문자 구분O (Member, age)

      > JPQL 키워드는 대소문자 구분X (SELECT, FROM, where)

      > 엔티티 이름 사용, 테이블 이름이 아님(Member)

      > 별칭은 필수(m) (as는 생략가능)

      > 집합함수 : select COUNT(m), SUM(m.age), AVG(m.age), MAX(m.age), MIN(m.age) from Member m;

     

    [반환 타입]

    1) TypeQuery : 반환 타입이 명확할 때 사용

      > TypedQuery query = em.createQuery("SELECT m FROM Member m", Member.class);

    2) Query : 반환 타입이 명확하지 않을 때 사용

      > Query query = em.createQuery("SELECT m.username, m.age from Member m");

     

    [결과 조회 API]

    1) query.getResultList()

      > 결과가 하나 이상일 때 리스트 반환

      > 결과가 없으면 빈 리스트 반환

    2) query.getSingleResult()

      > 결과가 정확히 하나, 단일 객체 반환

      > 결과가 없으면: javax.persistence.NoResultException, 둘 이상이면: javax.persistence.NonUniqueResultException

     

    [페이징 API]

    1) setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)

    2) setMaxResults(int maxResult) : 조회할 데이터 수

     

    //페이징 쿼리

    String jpql = "select m from Member m order by m.name desc";

    List<Member> resultList = em.createQuery(jpql, Member.class)

             .setFirstResult(10)

             .setMaxResults(20)

             .getResultList();

     

    [파라미터 바인딩]

    1) 이름 기준

      SELECT m FROM Member m where m.username=:username

      > query.setParameter("username", usernameParam);

    2) 위치 기준

      SELECT m FROM Member m where m.username=?1

      > query.setParameter(1, usernameParam);

     

    [프로젝션]

      > SELECT 절에 조회할 대상을 지정하는 것
      > 프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타입)
      > 프로젝션 - 여러 값 조회 방법

        SELECT m.username, m.age FROM Member m

        1) Query 타입으로 조회

        2) Object[] 타입으로 조회

        3) new 명령어로 조회  

          • 단순 값을 DTO로 바로 조회
            SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m 
          • 패키지 명을 포함한 전체 클래스 명 입력
          • 순서와 타입이 일치하는 생성자 필요

     

    [JPQL 타입 표현]
      > 문자: ‘HELLO’, ‘She’’s’ 
      > 숫자: 10L(Long), 10D(Double), 10F(Float) 
      > Boolean: TRUE, FALSE 
      > ENUM: jpabook.MemberType.Admin (패키지명 포함) 
      > 엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)

     

    [조인]

    1) 내부 조인(INNER JOIN)

      > SELECT m FROM Member m [INNER] JOIN m.team t

    2) 외부 조인(OUTER JOIN)

      > SELECT m FROM Member m LEFT [OUTER] JOIN m.team t

    3) 세타 조인

      > select count(m) from Member m, Team t where m.username = t.name

    4) ON절

      (1) 조인 대상 필터링

        예) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인

        > JPQL: SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'

        > SQL: SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'

      (2) 연관관계 없는 엔티티 외부 조인(하이버네이트 5.1부터)

        예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인

        > JPQL: SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name

        > SQL: SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name

      5) 페치 조인(fetch join)

        > JPQL에서 성능 최적화를 위해 제공하는 기능(SQL 조인 종류 X)으로 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능(즉시 로딩), 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념

        > 페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로

          [JPQL] select m from Member m join fetch m.team

          [SQL] SELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID

     

        (한계점)

          (1) 페치 조인 대상에는 별칭을 줄 수 없다.

          (2) 둘 이상의 컬렉션은 페치 조인 할 수 없다.

          (3) 컬렉션을 페치 조인하면 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다. 

        > 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 하면, 페치 조인 보다는 일반 조인을 사용하고 필요 한 데이터들만 조회해서 DTO로 반환하는 것이 효과적이다.

     

    [서브쿼리]

    1) 지원 함수

      > [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참

      > {ALL | ANY | SOME} (subquery)

        • ALL 모두 만족하면 참

        • ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참

      > [NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

     

    2) 예제

      (1) 나이가 평균보다 많은 회원

        > select m from Member m where m.age > (select avg(m2.age) from Member m2)

      (2) 한 건이라도 주문한 고객

        > select m from Member m where (select count(o) from Order o where m = o.member) > 0

      (3) 팀A 소속인 회원

        > select m from Member m where exists (select t from m.team t where t.name = ‘팀A')

      (4) 전체 상품 각각의 재고보다 주문량이 많은 주문들

        > select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)

      (5) 어떤 팀이든 팀에 소속된 회원

        > select m from Member m where m.team = ANY (select t from Team t)

     

    3) 한계점

      (1) JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능

      (2) SELECT 절도 가능(하이버네이트에서 지원)

      (3) FROM 절의 서브 쿼리는 현재 JPQL에서 불가능

      (4) 조인으로 풀 수 있으면 풀어서 해결

     

    3. JPQL 함수

      > CONCAT, SUBSTRING, TRIM, LOWER, UPPER, LENGTH, LOCATE, ABS, SQRT, MOD, SIZE, INDEX(JPA 용도)

      > COALESCE: 하나씩 조회해서 null이 아니면 반환

        예) select coalesce(m.username,'이름 없는 회원') from Member m // 사용자 이름이 없으면 이름 없는 회원을 반환

      > NULLIF: 두 값이 같으면 null 반환, 다르면 첫번째 값 반환

        예) select NULLIF(m.username, '관리자') from Member m // 사용자 이름이 ‘관리자’면 null을 반환하고 나머지는 본인의 이름을 반환

      > CASE식

        1) 기본 CASE 식

        select case when m.age <= 10 then '학생요금'

                       when m.age >= 60 then '경로요금'

                       else '일반요금' end

          from Member m;

        2) 단순 CASE 식

        select case t.name

                       when '팀A' then '인센티브110%'

                       when '팀B' then '인센티브120%'

                       else '인센티브105%' end

          from Team t;

      > 사용자 정의 함수 호출 : 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.

     

    [다형성 쿼리]

    1) TYPE : 조회 대상을 특정 자식으로 한정

      > 예) Item 중에 Book, Movie를 조회해라

          [JPQL] select i from Item i where type(i) IN (Book, Movie)

          [SQL] select i from i where i.DTYPE in (‘B’, ‘M’) 

    2) TREAT(JPA 2.1) : 자바의 타입 캐스팅과 유사, 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용

      > FROM, WHERE, SELECT(하이버네이트 지원) 사용 TREAT(JPA 2.1)

      > 예) 부모인 Item과 자식 Book이 있다.

          [JPQL] select i from Item i where treat(i as Book).auther = ‘kim’

          [SQL] select i.* from Item i where i.DTYPE = ‘B’ and i.auther = ‘kim'

     

    4. Named Query

      > 미리 정의해서 이름을 부여하고 사용하는 JPQL(정적 쿼리)로 애플리케이션 로딩 시점에 쿼리를 검증한다.

      > 어노테이션 방식과 XML 방식이 존재한다. XML이 항상 우선권을 가지며, 애플리케이션 운영 환경에 따라 다른 XML을 배포할 수 있다.

    어노테이션
    XML

     

    5. 벌크 연산

      > 벌크 연산은 쿼리 한 번으로 여러 테이블 로우를 변경(엔티티)하는 것으로 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리한다. (UPDATE, DELETE 지원, INSERT(insert into .. select, 하이버네이트 지원))

      > 벌크 연산을 먼저 실행

      > 벌크 연산 수행 후 영속성 컨텍스트 초기화

    executeUpdate()의 결과는 영향받은 엔티티 수 반환

     

     

    반응형

    'BackEnd > JPA' 카테고리의 다른 글

    04. JPA 값 타입  (0) 2021.08.13
    03. JPA 연관관계 관리  (0) 2021.08.13
    02. JPA Mapping Annotation  (0) 2021.08.09
    01. JPA(Java Persistence API)  (0) 2021.08.09

    댓글

Designed by Tistory.