깃허브 바로가기
책의 내용이 너무많아 계속 분리해서 작성하게 된다.
이번 포스팅에서는 서브 쿼리부터 내용을 다뤄보도록 하겠다.
JPQL도 SQL처럼 서브 쿼리를 지원하는데, 여기서는 몇 가지 제약사항이 있다.
서브 쿼리를 WHERE, Having 절에서만 사용할 수 있고 select, from 절에서는 사용할 수 없다.
종류 | 설명 | 예제 |
---|---|---|
문자 | 작은 따옴표 사이에 표현, 작은 따옴표를 표현하고 싶다면 작은따옴표 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 |
where m.age between 10 and 20
where 뒤에 식으로 작성할 수 있다. Member의 나이가 10~20인 사람 찾기
In에는 서브쿼리를 사용할 수 있다. in절의 조건이 하나라도 있으면 참이다.
문자표현식과 패턴을 비교한다.
Null 인지 비교한다. Null은 =으로 비교하면 안되고 is null을 사용해야 한다.
컬렉션에만 사용하는 특별한 기능이다. 컬렉션은 컬렉션 식 이외에 다른 식은 사용할 수가 없다.
컬렉션에 값이 비었으면 참이다.
위의 수학 연산에 더해 아래와 같은 문자함수도 있다.
함수 | 설명 | 예제 |
---|---|---|
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 을 사용해야 할 수 있다. |
날짜 함수는 아래와 같다.
이렇게 보면 DB에서 사용하는 함수가 거의 다 문법이 비슷하게 사용되는 것을 볼 수 있다.
CASE식은 생략하고 추후에 내가 사용할 때 다시 정리해야 겠다.
JPQL로 부모 엔티티를 조회하면 자식 엔티티도 조회된다. Item의 자식으로 Book, Album, Movie 가 있다고 한다면 조회를 했을때 Item을 상속받는 Book, Album, Movie도 조회한다.
이걸 단일 테이블 전략을 사용하면 SQL이 select * from Item
이 되는데
조인 전략을 가져가면 left outer join이 세번 걸리게 된다.
TYPE은 엔티티의 상속 구조에서 조회 대상을 특정 자식 타입으로 한정할 때 주로 사용한다.
자바의 타입 캐스팅과 빗슷함. 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용.
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로 바꿔서 등록한다.
그렇게 이 구현체를 사용하면 해당하는 함수를 바로 사용할 수 있다.
Empty String
JPA표준은 ''을 길이가 0인 Empty String으로 정했지만 DB에 따라 ''를 Null로 사용하는 DB가 있으므로 확인하고 사용해야 한다.
Null 정의
객체 인스턴스는 참조 값으로 식별하고 테이블 로우는 기본 키 값으로 식별한다.
JPQL에서 엔티티 객체를 직접 사용하면 SQL에서는 해당 엔티티의 기본 키값을 사용한다.
em.createQuery("")
처럼 JPQL을 문자로 완성해서 직접 넘기는 것을 동적 쿼리라고 한다. 런타임에 특정 조건에 따라 JPQL을 동적으로 구성할 수 있다.Named쿼리는 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱해둔다. 그래서 오류를 빨리 확인할 수 있고, 사용하는 시점에 결과를 재사용하므로 성능상의 이점도 있다.
Named 쿼리는 정적 SQL이 생성되기 때문에 DB의 성능 최적화에 도움이 된다.
Named 쿼리는 @NamedQuery
를 사용하여 자바 코드에 작성하거나 XML문서에 작성할 수 있다.
하나 이상의 NamedQuery를 사용하려면 @NamedQueries 어노테이션 사용할 것.
Named 쿼리를 XML에 정의하는 부분은 XML 대신에 Java에서 많이 하려고 노력하자.