객체지향 쿼리 언어(1)

Jeongyeon Kim·2023년 2월 2일
0

JPA

목록 보기
9/11
post-thumbnail

1. 객체지향 쿼리 소개

JPQL

  • 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않음

JPQL 소개

  • 엔티티 객체를 조회하는 객체지향 쿼리
  • SQL을 추상화 해서 특정 데이터베이스에 의존하지 않음
  • SQL보다 간결(엔티티 직접 조회, 묵시적 조인, 다형성 지원)

Criteria 쿼리 소개

  • JPQL을 생성하는 빌더 클래스
  • 문자가 아닌 프로그래밍 코드로 JPQL 작성 가능 ➡️ 컴파일 시점에 오류 발견 가능
  • IDE를 사용하면 코드 자동완성 지원
  • 동적 쿼리 작성하기 편함

QueryDSL 소개

  • JPQL 빌더(JPA 표준은 아니고, 오픈소스 프로젝트)
  • 코드 기반이며 단순하고 사용하기 쉬움
  • 어노테이션 프로세서를 사용해서 쿼리 전용 클래스 만들어야 함

네이티브 SQL 소개

  • SQL을 직접 사용하는 기능
  • 특정 데이터베이스에 의존하는 SQL을 작성해야 한다는 단점

JDBC 직접 사용, 마이바티스 같은 SQL 매퍼 프레임워크 사용

  • JDBC나 마이바티스를 JPA와 함께 사용하면 영속성 컨텍스트를 적절한 시점에 강제로 플러시해야 함
  • 이를 해결하는 방법은 JPA를 우회해서 SQL을 실행하기 직전에 영속성 컨텍스트를 수동으로 플러시해서 데이터베이스와 영속성 컨텍스트를 동기화하면 됨

2. JPQL

기본 문법과 쿼리 API

SELECT문

  • 대소문자 구분
    • 엔티티와 속성은 대소문자 구분
    • SELECT, FROM, AS 같은 JPQL 키워드는 대소문자 구분X
  • 엔티티 이름
  • 별칭 필수

TypeQuery, Query

  • TypeQuery: 반환할 타입을 명확하게 지정 가능
  • Query: 반환할 타입 명확하게 지정 불가
  • Query 객체는 SELECT 절의 조회 대상이 둘 이상이면 Object[]를 반환하고 SELECT 절의 조회 대상이 하나면 Object 반환

결과 조회

  • query.getResultList(): 결과를 예제로 반환, 결과 없으면 빈 컬렉션 반환
  • query.getSingleResult(): 결과가 정확히 하나일 때 사용
    • 결과 없으면 javax.persistence.NoResultException 예외 발생
    • 결과가 1개보다 많으면 javax.persistence.NonUniqueResultException 예외 발생

파라미더 바인딩

  • 파라미터 바인딩은 선택 아닌 필수!
  • 이름 기준 파라미터: 파라미터를 이름으로 구분, 이름 기분 파라미터 앞에 : 사용
  • 위치 기준 파라미터: ? 다음에 위치 값 주면 됨

프로젝션

  • 프로젝션: SELECT 절에 조회할 대상을 지정하는 것
  • 대상: 엔티티, 임베디드 타입, 스칼라 타입
  • 엔티티 프로젝션
    • 조회한 엔티티는 영속성 컨텍스트에서 관리
  • 임베디드 타입 프로젝션
    • 임베디드 타입은 조회의 시작점이 될 수 없음
    • 임베디드 타입은 값 타입. 따라서 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리 X
  • 스칼라 타입 프로젝션
    • 스칼라 타입: 숫자, 문자, 날짜와 같은 기본 데이터 타입
    • 중복 데이터를 제거하려면 DISTINCT 사용
    • 통계 쿼리도 주로 스칼라 타입으로 조회
  • 여러 값 조회
    • 프로젝션에 여러 값을 선택하면 Query 사용
  • NEW 명령어
    • SELECT 다음에 NEW 명령어를 사용하면 반환 받을 클래스 지정 가능
      ➡️ NEW 명령어를 사용한 클래스로 TypeQuery 사용할 수 있어서 지루한 객체 변환 작업을 줄일 수 있음
    • 패키지 명을 포함한 전체 클래스 명을 입력해야 함
    • 순서와 타입이 일치하는 생성자 필요

페이징 API

  • setFirstResult (int startPosition): 조회 시작 위치(0부터 시작)
  • setMaxResults (int maxResult): 조회할 데이터 수

집합과 정렬

집합 함수

  • COUNT: 결과 수
  • MAX, MIN: 최대, 최소 값
  • AVG: 평균값
  • SUM: 합

집합 함수 사용 시 참고 사항

  • NULL 값은 무시하므로 통계에 잡히지 X
  • 값이 없는데 SUM, AVG, MAX, MIN 사용하면 NULL, COUNT는 0
  • DISTINCT를 집합 함수 안에 사용해서 중복된 값 제거하고 나서 집합 구할 수 있음
  • DISTINCT를 COUNT에서 사용할 때 임베디드 타입 지원 X

GROUP BY, HAVING

  • GROUP BY: 통계 데이터를 구할 때 특정 그룹끼리 묶어줌
  • HAVING: GROUP BY로 그룹화한 통계 데이터를 기준으로 필터링

정렬(ORDER BY)

  • ORDER BY: 결과 정렬
    • ASC: 오름차순
    • DESC: 내림차순

JPQL 조인

내부 조인

  • INNER JOIN = JOIN
  • JPQL 조인의 가장 큰 특징은 연관 필드 사용
  • 연관 필드: 다른 엔티티와 연관관계를 가지기 위해 사용하는 필드

외부 조인

  • LEFT JOIN = LEFT OUTER JOIN

컬렉션 조인

  • 일대다 관계나 다대다 관계처럼 컬렉션을 사용하는 곳에 조인

세타 조인

  • 내부 조인만 지원
  • WHERE 절 사용

JOIN ON 절

  • ON 절을 사용하면 조인 대상을 필터링하고 조인할 수 있음
  • 보통 외부 조인에서만 사용

페치 조인

  • JPQL에서 성능 최적화를 위해 제공
  • 연관된 엔티티나 컬렉션을 한 번에 같이 조회
  • join fetcch
  • 별칭 사용 불가
  • DISTINCT 명령어는 SQL에 DISTINCT를 추가하는 것은 물론이고 애플리케이션에서 한 번 더 중복 제거
  • JPQL은 결과를 반환할 때 연관관계까지 고려하지 않음. 단지 SELECT 절에 지정한 엔티티만 조회
  • 페치 조인을 사용하면 SQL 한 번으로 연관된 엔티티들을 함께 조회할 수 있어서 SQL 호출 횟수를 줄여 성능을 최적화할 수 있음
  • 연관된 엔티티를 쿼리 시점에 조회하므로 지연 로딩이 발생하지 않아 준영속 상태에서도 객체 그래프를 탐색할 수 있음
  • 둘 이상의 컬렉션을 페치할 수 없음: 컬렉션 * 컬렉션의 카테시안 곱이 만들어지므로 주의
  • 컬렉션을 페치 조인하면 페이징 API 사용 불가
  • 페치 조인은 객체 그래프를 유지할 때 사용하면 효과적
  • 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 한다면 여러 테이블에서 필요한 필드들만 조회해서 DTO로 반환하는 것이 더 효과적

경로 표현식

  • 상태 필드: 단순히 값을 저장하기 위한 필드
  • 연관 필드: 연관관계를 위한 필드, 임베디드 타입 포함
    • 단일 값 연관 필드: @ManyToOne, @OneToOne, 대상이 엔티티
    • 컬렉션 값 연관 필드: @OneToMany, @ManyToMany, 대상이 컬렉션

경로 표현식과 특징

  • 상태 필드 경로: 경로 탐색의 끝
  • 단일 값 연관 경로: 묵시적으로 내부 조인이 일어남. 단일 값 연관 경로는 계속 탐색 가능
  • 컬렉션 값 연관 경로: 묵시적으로 내부 조인 일어남. 더는 탐색할 수 없음. FROM 절에서 조인을 통해 별칭을 얻으면 별칭으로 탐색 가능
  • 묵시적 조인은 모두 내부 조인

서브 쿼리

  • WHERE, HAVING 절에서만 사용가능
  • SELECT, FROM 절에서는 사용 불가

서브 쿼리 함수

  • EXISTS
  • {ALL | ANY | SOME}
  • IN

조건식

타입 표현

  • 문자
  • 숫자
  • 날짜
  • Boolean
  • Enum
  • 엔티티 타입

연산자 우선 순위

  1. 경로 탐색 연산(.)
  2. 수학 연산: +, -(단항 연산자), *, /, +, -
  3. 비교 연산: =, >, >=, <. <=. <>(다름), [NOT] BETWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY, [NOT] MEMBER [OF], [NOT] EXISTS
  4. 논리 연산: NOT, AND, OR

컬렉션 식

  • 컬렉션은 컬렉션 식 이외에 다른 식 사용 불가

CASE 식

  • 기본 CASE
CASE
	{WHEN <조건식> THEN <스칼라식>}+
   	ELSE <스칼라식>
END
  • 심플 CASE
CASE <조건대상>
	{WHEN <스칼라식1> THEN <스칼라식2>}+
    ELSE <스칼라식>
END
  • COALESCE
COALESCE(<스칼라식> {, <스칼라식>}+)
  • NULLIF
NULLIF(<스칼라식>, <스칼라식>)

다형성 쿼리

  • TYPE: 엔티티의 상속 구조에서 조회 대상을 특정 자식 타입으로 한정할 때 주로 사용
  • TREAT: 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용

사용자 정의 함수 호출

function_invocation::= FUNCTION(function_name {, function_arg}*)

기타 정리

  • enum은 = 비교 연산만 지원
  • 임베디드 타입은 비교 지원 X
  • EMPTY STRING
    • JPA 표준은 ''을 길이 0인 Empty String으로 정했지만 데이터베이스에 따라 ''를 NULL로 사용하는 데이터베이스도 있음
  • NULL 정의
    • 조건을 만족하는 데이터가 하나도 없으면 NULL
    • NULL은 알 수 없는 값. NULL과의 모든 수학적 계산 결과는 NULL
    • Null == Null은 알 수 없는 값
    • Null is Null은 참

엔티티 직접 사용

  • 기본 키 값
    • JPQL에서 엔티티 객체를 직접 사용하면 SQL에서는 해당 엔티티의 기본 키 값을 사용
  • 외래 키 값

Named 쿼리: 정적 쿼리

  • 동적 쿼리: em.createQuery("select ..") 처럼 JPQL을 문자로 완성해서 직접 넘기는 것, 런타임에 특정 조건에 따라 JPQL을 동적으로 구성 가능
  • 정적 쿼리: 미리 정의한 쿼리에 이름을 부여해서 필요할 대 사용, Named 쿼리는 한 번 정의하면 변경할 수 없는 정적 쿼리
  • Named 쿼리는 애플리케이션 도이 시점에 JPQL 문법을 체크하고 미리 파싱
    ➡️ 오류 빨리 확인 가능, 파싱된 결과를 재사용 하므로 성능상 이점, 변하지 않는 정적 SQL이 생성되므로 데이터베이스의 조회 성능 최적화에 도움
  • 어노테이션에 정의: @NamedQuery
  • XML에 정의
  • XML이 우선권 가짐
profile
Backend Developer👩🏻‍💻

0개의 댓글