깃허브 바로가기
책의 내용이 너무많아 계속 분리해서 작성하게 된다.
이번 포스팅에서는 서브 쿼리부터 내용을 다뤄보도록 하겠다.
서브 쿼리
JPQL도 SQL처럼 서브 쿼리를 지원하는데, 여기서는 몇 가지 제약사항이 있다.
서브 쿼리를 WHERE, Having 절에서만 사용할 수 있고 select, from 절에서는 사용할 수 없다.
서브쿼리 함수
- [NOT] EXISTS 서브쿼리
- 서브쿼리에 결과가 존재하면 참. NOT은 반대
- {ALL | ANY | SOME} 서브쿼리
- 비교 연산자와 같이 사용한다.
- ALL: 조건을 모두 만족하면 참
- ANY or SOME : 둘은 같은 의미이다. 조건을 하나라도 만족하면 참
- 비교 연산자와 같이 사용한다.
- [NOT] IN 서브쿼리
- 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참. 참고로 IN은 서브쿼리가 아닌 곳에서도 사용한다.
조건식
종류 | 설명 | 예제 |
---|---|---|
문자 | 작은 따옴표 사이에 표현, 작은 따옴표를 표현하고 싶다면 작은따옴표 2개('') 사용 | 'Hello', 'He''s' |
숫자 | L(Long), D(Double), F(Float) | 10L, 10D, 10F |
날짜 | Date {d 'yyyy-mm-dd'} Time {t 'hh-mm-ss'} DateTime {ts 'yyyy-mm-dd hh:mm:ss.f'} |
{d '2021-07-25'} {t '15:00:12'} {ts '2021-07-25 15:48:22.123} m.createDate = {d '2021-07-25'} |
Boolean | TRUE, FALSE | - |
Enum | 패키지명을 포함한 전체 이름을 사용해야 한다. | pack.MemberType.Customer |
엔티티 타입 | 엔티티의 타입을 표현한다. 주로 상속과 관련해서 사용한다. | TYPE(m) = Member |
연산자 우선 순위
- 경로 탐색 연산(.)
- 수학 연산 : 단항 연산자: +, -, 사칙연산 : *, /, +, -
- 비교 연산 : =, >=, >, 등, <>(다름), Between, Like, In, Is Null, Is Empty, Exists
- 논리 연산 : NOT, AND, OR
Between
where m.age between 10 and 20
where 뒤에 식으로 작성할 수 있다. Member의 나이가 10~20인 사람 찾기
In
In에는 서브쿼리를 사용할 수 있다. in절의 조건이 하나라도 있으면 참이다.
Like
문자표현식과 패턴을 비교한다.
- % : 아무 값들이 입력되어도 된다. (값이 없어도 됨)
- _ : 한 글자는 아무 값이 입력되어도 되지만 값이 있어야 한다.
NULL
Null 인지 비교한다. Null은 =으로 비교하면 안되고 is null을 사용해야 한다.
컬렉션 식
컬렉션에만 사용하는 특별한 기능이다. 컬렉션은 컬렉션 식 이외에 다른 식은 사용할 수가 없다.
Is Empty
컬렉션에 값이 비었으면 참이다.
스칼라 식
위의 수학 연산에 더해 아래와 같은 문자함수도 있다.
함수 | 설명 | 예제 |
---|---|---|
CONCAT(1, 2) | 문자를 합한다. | CONCAT('A','B') = AB |
SubString(문자, 위치, [길이]) | 길이 값이 없으면 나머지 전체 길이를 뜻한다. | SUBSTRING('ABCDEF', 2, 3) = BCD |
TRIM([[LEADING] , TRAILING , BOTH] [트림문자] FROM] 문자) | LEADING: 왼쪽만, TRAILING: 오른쪽만, BOTH: 양쪽 다 트림 문자를 제거한다. 기본값은 Both, 트림문자 기본값은 공백이다. | TRIM(' ABC ') = 'ABC' |
LOWER(문자) | 소문자로 변경 | |
UPPER(문자) | 대문자로 변경 | |
LENGTH(문자) | 문자 길이 | |
LOCATE(찾을 문자, 원본 문자, [검색시작위치]) | 검색 위치부터 문자를 검색한다. 1부터 시작, 못 찾으면 0을 반환 | LOCATE('AB', 'ABCDE') = 1 |
함수 | 설명 |
---|---|
ABS() | 절대값 |
SQRT() | 제곱근 |
MOD(수학식, 나눌수) | 나머지 |
SIZE(컬렉션 값 연관 경로식) | 컬렉션 크기 구함 |
INDEX(별칭) | LIST 타입 컬렉션의 위치값 구함, 컬렉션이 @OrderColumn 을 사용해야 할 수 있다. |
날짜 함수는 아래와 같다.
- Current_Date: 현재 날짜
- Current_Time: 현재 시간
- Current_TimeStamp: 현재 날짜 시간
이렇게 보면 DB에서 사용하는 함수가 거의 다 문법이 비슷하게 사용되는 것을 볼 수 있다.
CASE식은 생략하고 추후에 내가 사용할 때 다시 정리해야 겠다.
다형성 쿼리
JPQL로 부모 엔티티를 조회하면 자식 엔티티도 조회된다. Item의 자식으로 Book, Album, Movie 가 있다고 한다면 조회를 했을때 Item을 상속받는 Book, Album, Movie도 조회한다.
이걸 단일 테이블 전략을 사용하면 SQL이 select * from Item
이 되는데
조인 전략을 가져가면 left outer join이 세번 걸리게 된다.
TYPE
TYPE은 엔티티의 상속 구조에서 조회 대상을 특정 자식 타입으로 한정할 때 주로 사용한다.
TREAT
자바의 타입 캐스팅과 빗슷함. 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용.
JPA 표준은 FROM, Where 절에서 사용할 수 있지만, Hibernate는 Select 절에서도 Treat를 사용할 수 있다.
사용자 정의 함수 호출
JPA2.1 버전부터 사용자 정의 함수를 지원한다.
문법은 다음과 같다.function_invocation::= FUNCTION(function_name {, function_arg}*)
예) select function('group_concat', i.name) from Item i
Hibernate 구현체를 사용하면 방언클래스를 상속해서 구현하고 사용할 DB함수를 미리 등록해야 한다.
spring.jpa.hibernate.dialect: org.hibernate.dialect.H2Dialect
방언에따라 dialect 뒤를 해당 SQL로 바꿔서 등록한다.
그렇게 이 구현체를 사용하면 해당하는 함수를 바로 사용할 수 있다.
기타 정리
- enum은 =비교 연산만 지원한다.
- 임베디드 타입은 비교를 지원하지 않는다.
Empty String
JPA표준은 ''을 길이가 0인 Empty String으로 정했지만 DB에 따라 ''를 Null로 사용하는 DB가 있으므로 확인하고 사용해야 한다.
Null 정의
- 조건을 만족하는 데이터가 하나도 없으면 Null
- Null은 알수 없는 값이다. Null과의 모든 수학적 계산 결과도 Null이 된다.
- Null == Null 은 알수 없는 값이다.
- Null is Null은 참이다.
엔티티 직접 사용
기본 키 값
객체 인스턴스는 참조 값으로 식별하고 테이블 로우는 기본 키 값으로 식별한다.
JPQL에서 엔티티 객체를 직접 사용하면 SQL에서는 해당 엔티티의 기본 키값을 사용한다.
Named 쿼리 : 정적 쿼리
- 동적 쿼리:
em.createQuery("")
처럼 JPQL을 문자로 완성해서 직접 넘기는 것을 동적 쿼리라고 한다. 런타임에 특정 조건에 따라 JPQL을 동적으로 구성할 수 있다. - 정적 쿼리: 미리 정의한 쿼리에 이름을 부여해서 필요할 때 사용할 수 있는데 Named 쿼리라고 한다. Named쿼리는 한번 정의하면 변경할 수 없는 정적인 쿼리다.
Named쿼리는 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱해둔다. 그래서 오류를 빨리 확인할 수 있고, 사용하는 시점에 결과를 재사용하므로 성능상의 이점도 있다.
Named 쿼리는 정적 SQL이 생성되기 때문에 DB의 성능 최적화에 도움이 된다.
Named 쿼리는 @NamedQuery
를 사용하여 자바 코드에 작성하거나 XML문서에 작성할 수 있다.
- lockMode: 쿼리 실행 시 락을 건다.
- hints: 여기서의 힌트는 SQL 힌트가 아니라 JPA 구현체에게 제공하는 힌트이다. 2차 캐시를 다룰때 사용한다.
하나 이상의 NamedQuery를 사용하려면 @NamedQueries 어노테이션 사용할 것.
Named 쿼리를 XML에 정의하는 부분은 XML 대신에 Java에서 많이 하려고 노력하자.
'JPA' 카테고리의 다른 글
[JPA] 객체 지향 쿼리 언어 - Querydsl 2 (0) | 2022.08.05 |
---|---|
[JPA] 객체 지향 쿼리 언어 - Querydsl (0) | 2022.08.05 |
[JPA] 객체 지향 쿼리 언어 - 2 (0) | 2022.08.05 |
[JPA] 객체지향 쿼리 언어 - 1 (0) | 2022.08.05 |