객체지향 쿼리 언어(JPQL)

개발하는 도비·2023년 4월 29일

JPA

목록 보기
8/13
post-thumbnail

JPQL 소개

  • 단순 조회

    • EntityManager.find()
    • 객체 그래프 탐색(a.getB().getC())
  • JPA를 사용하면 entity 객체를 중심으로 개발

    • 검색 쿼리에서 문제
      • 검색 시 테이블이 아닌 entity 객체를 대상으로 검색
      • 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
      • 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요
  • JPQL : JPA에서 SQL을 추상화한 객체 지향 쿼리 언어

    • SELECT, FROM, WHERE, GROUP BY,HAVING, JOIN 지원
    • entity 객체를 대상으로 쿼리
    • 추상화로 인해 특정 데이터베이스 SQL 의존 X
  • JPQL 어려움

    • 결국 String이기에 동적으로 작성하기가 어려워짐.
    • Criteria, QueryDSL등으로 코드로 작설할 수 있음
    • 하지만 기반이 JPQL이기에 기본적으로 JPQL알아야함.

JPQL - 기본 문법과 기능

JPQL 문법

  • entiry와 속성은 대소문자 구분
  • JPQL 키워드는 대소문자 구분X
    • SELECT, FROM 등등
  • entity 이름 사용 -> 쿼리문에 사용하는 것은 테이블이 아님.
  • 별칭 필수 사용

집합과 정렬

  • COUNT, SUM, AVG, MAX, MIN
  • GROUP BY, HAVING, ORDER BY

TypeQuery, Query

  • TypeQuery: 반환 타입이 명확할 때 사용
    TypedQuery<Entity> query = em.createQuery("SELECT m FROM Entity m", Entity.class);
  • Query: 반환 타입이 명확하지 않을 때 사용
    Query query = em.createQuery("SELECT m.username, m.age from Entity m");

결과 조회

  • query.getResultList()
    • 결과가 하나 이상일 때
    • 리스트 반환
    • 결과가 없으면 빈 리스트 반환
  • query.getSingleResult()
    • 결과가 정확히 하나
    • 단일 객체 반환
    • 결과 X: javax.persistence.NoResultException
    • 결과 복수 : javax.persistence.NonUniqueResultException

파라미터 바인딩

  • query.setParameter() 사용
  • 이름 기반
  • 예시
    SELECT m FROM Member m where m.username=:username query.setParameter("username", usernameParam);
  • 위치 기반
    • 권장되지 않음. 밀릴 가능성이 있음.
    SELECT m FROM Member m where m.username=?1 query.setParameter(1, usernameParam);

프로젝션

개념 및 종류

  • SELECT 절에 조회할 대상을 지정하는 것
  • 종류
    • 엔티티 프로젝션
    • 임베디드 타입 프로젝션
    • 스칼라 타입 프로젝션
  • DISTINCT로 중복 제거

여러 값 조회

  • Query 타입으로 조회

    • query.getResultList() 사용
    • 타입 casting 필요
    List resultList = em.createQuery("select m.name, m.age from Member m").getResultList();
    
    Object o = resultList.get(0);
    Object[] result = (Object[]) o;
  • Object[] 타입으로 조회

    List<Object[]> resultList = em.createQuery("select m.name, m.age from Member m").getResultList();
    Object[] result = resultList.get(0);
  • new 명령어로 조회

    SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
    • 순서와 타입이 일치하는 생성자 필요

페이징 API

  • JPA는 페이징을 두 API로 추상화
    • setFirstResult(int startPosition) : 조회 시작 위치
    • setMaxResults(int maxResult) : 조회할 데이터 수

조인

  • 내부 조인:
    SELECT m FROM Member m [INNER] JOIN m.team t
  • 외부 조인:
    SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
  • 세타 조인:
    select count(m) from Member m, Team t where m.username= t.name

ON 절

  • 조인 대상 필터링
    • 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
      SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
  • 연관관계 없는 entity 외부 조인
    SELECT m, t FROM
    Member m LEFT JOIN Team t on m.username = t.name

서브 쿼리

  • 쿼리 내부에 한번 더 쿼리

  • 지원 함수

    • EXISTS : 결과가 존재 -> 참
      • ALL : 모두 만족 -> 참
      • ANY, SOME : 하나라도 만족 -> 참
    • IN : 하나라도 같은 것이 존재 -> 참
  • JPA 서브 쿼리 한계

    • WHERE, HAVING, SELECT(하이버네이트) 절에서만 가능
    • FROM 절의 서브 쿼리는 현재 JPQL에서 불가능
      • 조인으로 풀 수 있으면 풀어서 해결
      • 하이버네이트6부터 가능

JPQL 타입 표현과 기타식

JPQL 타입 표현

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

JPQL 기타

  • SQL과 문법이 같은 식
  • EXISTS, IN
  • AND, OR, NOT
  • =, >, >=, <, <=, <>
  • BETWEEN, LIKE, IS NULL

조건식

CASE 식

  • 기본 CASE 식
    select
      case when m.age <= 10 then '학생요금' when m.age >= 60 then '경로요금'
        else '일반요금'
      end
    from Member m
  • 단순 CASE 식
    select
      case t.name
        when '팀A' then '인센티브110%' 
        when '팀B' then '인센티브120%'
        else '인센티브105%'
      end
    from Team t
  • COALESCE : 하나씩 조회해서 null이 아니면 반환
    select coalesce(m.username,'이름 없는 회원') from Member m
  • NULLIF : 두 값이 같으면 null 반환, 다르면 첫번째 값 반환
    select NULLIF(m.username, '관리자') from Member m

JPQL 함수

JPQL 기본 함수

  • DB와 상관없이 사용
    • CONCAT
    • SUBSTRING
    • TRIM
    • LOWER, UPPER
    • LENGTH
    • LOCATE
    • ABS, SQRT, MOD
    • SIZE, INDEX(JPA 용도)

사용자 정의 함수 호출

  • 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록.
  • function('function name')

경로 표현식

  • .을 찍어 객체 그래프를 탐색하는 것
  • 상태 필드(state field)
    • 단순히 값을 저장하기 위한 필드 (ex: m.username)
    • 경로 탐색의 끝, 탐색X
  • 연관 필드(association field): 연관관계를 위한 필드
    • 단일 값 연관 필드:
      • @ManyToOne, @OneToOne, 대상이 entity(ex: m.team)
      • 묵시적 내부 조인(inner join) 발생, 탐색O
        • m.team -> member 과 team join 발생.
    • 컬렉션 값 연관 필드:
      • @OneToMany, @ManyToMany, 대상이 컬렉션(ex: m.orders)
      • 묵시적 내부 조인 발생, 탐색X
        • 컬렉션 자체를 가르키기에 추가 탐색 불가
        • size는 가능
        • FROM절 명시적 조인 -> 별칭 획득 -> 탐색 가능
  • 묵시적 조인 시 주의사항
    • 항상 내부 조인
    • 컬렉션은 명시적 조인을 통해 별칭을 얻어야함
    • 경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만 묵시적 조인으로 인해 SQL의 FROM (JOIN) 절에 영향을 줌
  • 실무 조언
    • 명시적 조인 사용
    • 조인은 SQL 튜닝에 중요 포인트 -> 성능 영향
    • 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어려움

페치 조인(fetch join)

  • SQL 조인 종류X
  • 성능 최적화를 위해 제공
  • 연관된 entity나 컬렉션을 SQL 한 번에 함께 조회
  • join fetch 사용
  • 지연 로딩X -> 한번에 함께 조회하기 때문.

entity 페치 조인

  • 특별한 특징은 안보임 -> 기본적인 특징만으러 이해 가능.

컬렉션 페치 조인

  • 컬렉션으로 페치 조인 했기에 데이터가 일정의 뻥튀기 됨. -> 일대다 이기 때문.

페치 조인과 DISTINCT

  • SQL에 DISTINCT를 추가
  • 애플리케이션에서 엔티티 중복 제거

페치 조인의 특징과 한계

  • 페치 조인 대상에는 별칭을 줄 수 없다.(관례)
    • 만들어진 목적 자체가 연관된 entity나 컬렉션을 한번에 조회하는 것이기 때문.
    • 일부를 가져올 경우 페치 조인 사용 x
  • 둘 이상의 컬렉션은 페치 조인 x.
    • 일대다 -> 뻥튀기 => 두개일 경우 얼마나 데이터가 늘어날지 모름.
  • 컬렉션을 페치 조인하면 페이징 API 사용 불가
    • 일대일,다대일 같은 단일값 연관필드들은 페치조인해도 페이징 가능

다형성 쿼리

  • TYPE : 조회 대상을 특정 자식으로 한정
  • TREAT
    • 자바의 타입 캐스팅과 유사
    • 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용
    • FROM, WHERE, SELECT

엔티티 직접 사용

  • JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기 본 키 값을 사용
    • entity 와 entity.id 같은 결과를 보임.

Named 쿼리

  • 미리 정의해서 이름을 부여해두고 사용하는 JPQL

  • 정적 쿼리

  • 어노테이션, XML에 정의

  • 애플리케이션 로딩 시점에 초기화 후 재사용

  • 애플리케이션 로딩 시점에 쿼리를 검증 -> 어노테이션에 등록되기에 문법 오류가 날 경우 알려줌.

  • 우선 순위는 XML -> 어노테이션

벌크 연산

  • 쿼리 한번에 여러개를 업데이트.
  • executeUpdate()를 사용
  • 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
    • 벌크 연산을 먼저 실행
    • 벌크 연산 수행 후 영속성 컨텍스트 초기화

참조

  • 인프런 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
  • 링크
profile
도비의 양말을 찾아서

0개의 댓글