프레임워크(Framework)/JPA

[JPA] JPQL 타입 표현과 기타식, 조건식 - 객체지향 쿼리 언어 JPQL (6)

잇트루 2022. 12. 18. 18:16
반응형
본 내용은 온라인 강의 사이트 인프런의 김영한 님의 강의 내용이 포함되어 있습니다.
'자바 ORM 표준 JPA 프로그래밍 - 기본편'
 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

타입 표현

JPQL에서 사용하는 타입은 다음과 같이 표시할 수 있으며, 대소문자는 구분하지 않는다.

 

문자

  • 작은 따옴표(’) 사이에 표현
  • 작은 따옴표(’)는 두 번 연속 사용하여 표현
  • ex) ‘HELLO’, ‘She’’s’

 

숫자

  • L(Long 타입), D(Double 타입), F(Float 타입)
  • ex) 10L, 10D, 10F, 10

 

날짜

  • DATE {d ‘yyyy-mm-dd’}, TIME {t ‘hh-mm-ss’}, DATETIME(ts ‘yyyy-mm-dd hh:mm:ss.f’}
  • ex) {d ‘2022-12-05’}, {t ‘10-30-59’}, {ts ‘2022-12-05 10:30:59’}
  • m.createDate = {d ‘2022-12-05’}

 

Boolean

  • TRUE, FALSE

 

Enum

  • 패키지명을 포함한 전체 이름을 사용해야 한다.
  • ex) jpabook.MemberType.Admin

 

엔티티 타입

  • 엔티티 타입을 표현, 주로 상속과 관련하여 사용한다.
  • TYPE(m) = Member

Item 엔티티와 상속관계인 Book이 있다고 가정한 경우, 엔티티 타입이 Book인 Item 조회

em.createQuery("select i from Item i where type(i) = Book", Item.class);

 

타입 표현 간단 예시

Enum 타입이 ADMIN인 멤버에 대한 쿼리

  • Enum 타입은 패키지명을 포함한 전체 이름을 사용해야 한다.
String query = "select m.username, 'HELLO', TRUE, false from Member m where m.type = jpql.MemberType.ADMIN";

List<Object[]> result = em.createQuery(query).getResultList();

for (Object[] objects : result) {
    System.out.println("objects[0] = " + objects[0]); // m.username
    System.out.println("objects[1] = " + objects[1]); // HELLO
    System.out.println("objects[2] = " + objects[2]); // TRUE
    System.out.println("objects[3] = " + objects[3]); // FALSE
}
// 출력
objects[0] = member1
objects[1] = HELLO
objects[2] = true
objects[3] = false

 

Enum 타입의 경우 파라미터 바인딩을 통해 조회할 수도 있다.

String query = "select m.username, 'HELLO', TRUE, false from Member m where m.type = :userType";

List<Object[]> result = em.createQuery(query)
        .setParameter("userType", MemberType.ADMIN) // 파라미터 바인딩
        .getResultList();

for (Object[] objects : result) {
    System.out.println("objects[0] = " + objects[0]);
    System.out.println("objects[1] = " + objects[1]);
    System.out.println("objects[2] = " + objects[2]);
    System.out.println("objects[3] = " + objects[3]);
}

 

 

기타식

JPQL은 SQL과 문법이 같은 식을 지원한다.

서브쿼리

  • [NOT] EXISTS (subquert) : 서브 쿼리가 존재하면 참. NOT은 반대
  • [NOT] IN (subquery) : 서브 쿼리의 결과 중 하나라도 같은 것이 있으면 참
  • {ALL | ANY | SOME} (subquery) : 비교 연산자와 같이 사용
    • ALL : 조건을 모두 만족하면 참
    • ANY, SOME : 조건을 하나라도 만족하면 참(ANY와 SOME 같은 의미)

 

논리 연산

  • AND : 둘 다 만족하면 참
  • OR : 둘 중 하나만 만족해도 참
  • NOT : 조건식 결과의 반대

 

비교 연산

  • =, >, ≥, <, ≤, <>

 

Between 식

  • BETWEEN : A ~ B 사이의 값이면 참(A, B 포함)
  • LIKE : 문자 표현식과 패턴 값을 비교
  • IS [NOT] NULL : NULL 인지 비교

 

컬렉션 식

  • IS [NOT] EMPTY : 컬렉션에 값이 비어있는지 여부
  • [NOT] MEMBER [OF] : 엔티티나 값이 컬렉션에 포함되어 있는지 여부

 

스칼라 식

  • 단항 연산자(+, -), 사칙연산(+, -, *, /)

 

기타식 간단 예시

// IS NULL : 이름이 null이 아닌 Member 이름 조회
em.createQuery("select m.username from Member m where m.username is not null").getResultList();

// BETWEEN : 나이가 20 ~ 30인 Member 이름 조회
em.createQuery("select m.username from Member m where m.age between 20 and 30").getResultList();

// LIKE : 이름이 박으로 시작하는 Member 이름 조회
em.createQuery("select m.username from Member m where m.username like '박%'").getResultList();

// IN : 이름이 홍길동이나 임꺽정인 Member 이름 조회
em.createQuery("select m.username from Member m where m.username in ('홍길동', '임꺽정')".getResultList();

// EMPTY : 주문이 하나라도 있는 Member 이름 조회
em.createQuery("select m.username from Member m where m.orders is not empty").getResultList();

 

연산자 우선순위

  1. 경로 탐색 연산(.)
  2. 수학 연산(+, -, *, /)
  3. 비교 연산(Between 식, 컬렉션 식 포함)
  4. 논리 연산(AND, OR, NOT)

 

 

기본 함수

문자 함수

  • CONCAT(문자1, 문자2, …) : 문자를 합한다.
    • concat('A', 'B') = AB

 

  • SUBSTRING(문자, 위치, [길이]) : 위치부터 시작해 길이만큼 문자를 구한다.
    • substring('ABCDEF', 2, 3) = BCD

 

  • TRIM([[LEADING | TRAILING | BOTH] [트림 문자] FROM] 문자) : 트림 문자를 제거한다.
    • LEADING : 트림 문자의 왼쪽 제거
    • TRAILING : 트림 문자의 오른쪽 제거
    • BOTH : 트림 문자의 양쪽 제거(기본값)
    • trim(’ ABC ‘) = ‘ABC’

 

  • LOWER(문자) : 소문자로 변경
    • lower(’ABC’) = ‘abc’

 

  • UPPER(문자) : 대문자로 변경
    • upper(’abc’) = ‘ABC’

 

  • LENGTH(문자) : 문자 길이
    • length(’abc’) = 3

 

  • LOCATE(찾을 문자, 원본 문자, [검색 시작 위치]) : 검색 위치부터 문자를 검색한다.
    • locate(’de’, ‘abcdef’) = 4
    • locate(’d’, ‘abc’) = 0

 

수학 함수

  • ABS(수학식) : 절댓값, abs(-10) = 10
  • SQRT(수학식) : 제곱근, sqrt(4) = 2.0
  • MOD(수학식, 나눌 수) : 나머지, mod(4, 3) = 1
  • SIZE(컬렉션 값 연관 경로식) : 컬렉션의 크기, size(t.members)
  • INDEX(별칭) : LIST 타입 컬렉션의 위치 값
    • 컬렉션이 @OrderColumn을 사용하는 LIST 타입일 때만 사용 가능
    • t.members m where index(m) > 3

 

사용자 정의 함수

하이버네이트는 사용 전 방언에 추가해야 한다.

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

 

group_concat이라는 함수가 있다고 가정한다.

public class MyH2Dialect extends H2Dialect {

    public MyH2Dialect() {
        registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
    }
}
  • 사용하는 데이터베이스의 Dialect를 상속받는 클래스를 작성하여 생성자에서 registerFunction() 메서드를 통해 함수를 등록한다.

 

사용자 정의 함수 호출

select function('group_concat', m.username) from Member m

// 하이버네이트 사용시 가능
select group_concat(m.username) from Member m

 

 

조건식 - CASE

JPQL은 SQL과 마찬가지로 특정 조건에 따라 분기할 때 CASE 식을 사용한다.

CASE 4가지 종류

  • 기본 CASE
  • 단순 CASE
  • COALESCE
  • NULLIF

 

기본 CASE

select
	case when m.age <= 10 then '학생요금'
         when m.age >= 60 then '경로요금'
         else '일반요금'
    end
from Member m

 

단순 CASE

단순 CASE는 조건식을 사용할 수 없지만, 문법이 단순하다. (자바의 switch case 문과 비슷)

select
    case t.name
        when 'TeamA' then '인센티브110%'
        when 'TeamB' then '인센티브120%'
        else '인센티브105%'
    end
from Team t

 

COALESCE

스칼라식을 차례대로 조회해서 null이 아니면 반환한다.

select coalesce(m.username, '이름 없는 회원') from Member m
  • m.username이 null이면 ‘이름 없는 회원’을 반환

 

NULLIF

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

select NULLIF(m.username, '관리자') from Member m
  • m.username이 ‘관리자’이면 null을 반환하고, null이 아니면 m.username 반환
반응형