일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 영속성
- java
- 교육기획팀원
- JPQL
- delayed message plugin
- kusitms
- 자동처리
- ddd
- 자바 ORM 표준 JPA 프로그래밍
- RESTClient
- 최범균
- 큐시즘
- 교육기획팀
- 한국대학생it경영학회
- JPA
- GitHub Actions
- Spring
- 밋업프로젝트
- Spring Batch
- 객체지향 쿼리 언어
- jdbc
- 30기
- springboot
- 이펙티브자바
- rabbitmq-delayed-message-exchange
- scheduling messages with rabbitmq
- Domain Driven Design
- cicd
- 도메인 주도 개발 시작하기
- reactive operaton
- Today
- Total
코딩은 마라톤
[자바 ORM 표준 JPA 프로그래밍] 10장. JPQL (2) 본문
경로 표현식
경로표현식이란 .(점)을 찍어 객체 그래프를 탐색하는 것이다.
select m.username
from Member m
join m.team t
join m.orders o
where t.name = '팀A'
위 쿼리에서 m.username, m.team, m.orders, t.name 모두 경로 표현식을 사용한 예다.
용어 정리
- 상태 필드 : 단순히 값을 저장하기 위한 필드 (필드 or 프로퍼티)
- 연관 필드 : 연관관계를 위한 필드, 임베디드 타입 포함 (필드 or 프로퍼티)
- 단일 값 연관 필드 : @ManyToOne, @OneToOne, 대상이 엔티티
- 컬렉션 값 연관 필드 : @OneToMany, @ManyToMany, 대상이 컬렉션
경로 표현식과 특징
- 상태 필드 경로 : 경로 탐색의 끝이다. 더는 탐색할 수 없다.
select m.username, m.age from Member m
- 단일 값 연관 경로 : 묵시적으로 내부 조인이 일어난다. 계속 탐색 가능
- 묵시적 조인은 모두 내부 조인이다.
- 명시적 조인 : JPQL에 JOIN을 직접 적어 주는 것
- 묵시적 조인 : 경로 표현식에 의해 묵시적으로 조인이 일어남 (하기 코드)
// JPQL
select o.member from Order o
// SQL
select m.*
from Orders o
inner join Member m on o.member_id=m.id
- 컬렉션 값 연관 경로 : 묵시적으로 내부 조인이 일어난다. 더는 탐색할 수 없으나 FROM 절에서 조인을 통해 별칭을 얻으면 별칭으로 탐색 가능
select t.members from Team t // 성공
select t.members.username from Team t // 실패
t.members 처럼 컬렉션 까지만 경로 탐색이 가능하며 그 다음 경로를 탐색할 순 없다.
select m.username from Team t join t.members m // t.members에 새로운 별칭 추가
또한 컬렉션은 컬렉션 크기를 구할 수 있는 size라는 특별한 기능을 사용할 수 있다. (COUNT 함수)
묵시적 조인 시 주의사항
- 항상 내부 조인이다.
- 컬렉션은 경로 탐색의 끝이다. 컬렉션에서 경로 탐색 시 명시적 조인을 통해 별칭을 얻어야 한다.
- 경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만 묵시적 조인으로 인해 SQL FROM 절에 영향을 준다.
따라서 성능이 중요하면 묵시적 조인보다는 명시적 조인을 사용하여 분석하자.
서브 쿼리
서브쿼리는 WHERE, HAVING 절에서만 사용할 수 있고 SELECT, FROM 절에서는 사용할 수 없다.
// 나이가 평균보다 많은 회원을 찾는 경우
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
서브 쿼리 함수
- EXISTS
- 문법 : [NOT] EXISTS (subquery)
- 설명 : 서브쿼리에 결과가 존재하면 참이다.
- {ALL | ANY | SOME}
- 문법 : {ALL | ANY | SOME} (subquery)
- 비교 연산자와 같이 사용한다.
- ALL : 조건을 모두 만족하면 참이다.
- ANY, SOME : 조건을 하나라도 만족하면 참이다.
// 어떤 팀이든 팀에 소속된 멤버
select m from Member m
where m.team = ANY (select t from Team t)
- IN
- 문법 : [NOT] IN (subquery)
- 설명 : 서브쿼리의 결과 중 하나라도 같은 것이 있음 참이다. IN은 서브 쿼리가 아닌 곳에서도 사용한다.
// 20세 이상 멤버를 가진 팀
select t from Team t
where t IN (select t2 From Team t2 JOIN t2.members m2 where m2.age >= 20)
조건식
타입 표현 (모르는 부분만 정리)
- 문자 : 작은 따옴표로 표현, 작은 따옴표를 표현하고 싶으면 작음 따옴표 연속 2개 사용(''), 'She''s'
- Enum : 패키지명을 포함한 전체 이름을 사용한다.
- 엔티티 타입 : 엔티티의 타입을 표현한다. 주로 상속과 관련해서 사용한다. TYPE(m) = Member
연산자 우선 순위
- 경로 탐색 연산 (.)
- 수학 연산 : +, -, ...
- 비교 연산 : =, >, ... , BETWEEN, LIKE, IN, IS NULL, IS EMPTY, EXISTS...
- 논리 연산 : NOT, OR, AND
Between, IN, Like, NULL 비교
- Between 식
- 문법 : X [NOT] BETWEEN A AND B
- 설명 : X는 A ~ B 사이의 값이면 참 (A, B 포함)
- IN 식
- 문법 : X [NOT] IN
- 설명 : X와 같은 값이 예제에 하나라도 있으면 참
select m from Member M where m.username in ('한', '성')
- Like 식 (정규표현식 느낌)
- 문법 : 문자표현식 [NOT] LIKE 패턴값 [ESCAPE 이스케이프 문자]
- 설명 : 문자표현식과 패턴값을 비교한다.
- % : 아무 값들이 입력되어도 된다. (값이 없어도 됨)
- _ : 한 글자는 아무 값이 입력되어도 되지만 값은 있어야 한다
// 회원, A회원..
select m from Member m
where m.username like '%회원'
- NULL 비교식
- 문법 : {단일값 경로 | 입력 파라미터} IS [NOT] NULL
- 설명 : NULL인지 비교한다. NULL은 =으로 비교하지 말고 IS NULL을 사용한다.
컬렉션 식
컬렉션에서만 사용하는 기능이다.
- 빈 컬렉션 비교 식
- 문법 : { 컬렉션 연관 경로 } IS [NOT] EMPTY
- 설명 : 컬렉션에 값이 비었으면 참
//JPQL: 주문이 하나라도 있는 회원 조회
select m from Member m
where m.orders is not empty
// is empty 대신 is null을 사용하면 오류!
- 컬렉션의 멤버 식
- 문법 : { 엔티티, 값 } [NOT] MEMBER [OF] { 컬렉션 값 연관경로 }
- 설명 : 엔티티나 값이 컬렉션에 포함되어 있음 참
select t from Team t
where :memberParam member of t.members
스칼라 식
스칼라는 숫자, 문자, 날짜, 엔티티 타입 등 가장 기본적인 타입들을 말한다.
문자 함수
- CONCAT(문자1, 문자2, ...)
- 문자를 합한다.
- SUBSTRING(문자, 위치, [길이])
- 위치부터 시작해 길이만큼 문자를 구한다. 길이 값이 없으면 나머지 전체 길이를 뜻한다.
- SUBSTRING('ABCD', 2, 2) = BC
- TRIM (LEADING, TRAILING, BOTH)
- LEADING : 왼쪽만
- TRAILING : 오른쪽만
- BOTH : 양쪽 다 트림 문제를 제거 (default)
- TRIM(' SSF ') = SSF
- LOWER, UPPER : 소문자, 대문자 변경
- LENGTH : 문자 길이
- LOCATE(찾을 문자, 원본 문자, [검색시작위치])
- 검색위치부터 문자를 검색한다. 1부터 시작, 못찾으면 0 반환
- LOCATE('DE', ABCDE') = 4
수학 함수
- ABS : 절대값
- SQRT : 제곱근
- MOD (수학식, 나눌 수) : 나머지를 구한다.
- SIZE (컬렉션 값 연관 경로식) : 컬렉션 크기
- INDEX (별칭) : LIST 타입 컬렉션의 위치값을 구함. 컬렉션이 @OrderColumn을 사용하는 경우에만 사용
- t.members m where INDEX(M) > 3
날짜 함수
- CURRENT_DATE : 현재 날짜 | 2024-06-17
- CURRENT_TIME : 현재 시간 | 23:40:22
- CURRENT_TIMESTAMP : 현재 날짜 시간 | 2024-06-17 23:40:22.736
CASE 식
특정 조건에 따라 분기할 때 CASE 식을 사용한다.
- 기본 CASE
- 문법 : CASE { WHEN <조건식> THEN <스칼라식> } + ELSE <스칼라식> END
- 예시 : select case when m.age < 20 then '미성년자' else '성인' end from Member m
- 심플 CASE
- 조건식을 사용할 수 없는, switch case 문과 유사하다.
- 문법 : CASE <조건대상> when ... else... end
- COALESCE
- 문법 : COALESCE(<스칼라식> {, <스칼라식> } +)
- 설명 : 스칼라식을 차례로 조회해서 null이 아니면 반환
- 예시 : m.username이 null이면 '무명'을 반환할 때
- selece coalesce(m.username, '무명') from Member m
- NULLIF
- 문법 : NULLIF(<스칼라식>, <스칼라식>)
- 설명 : 두 스칼라 값이 같으면 null을 반환, 그렇지 않으면 첫 번째 스칼라 값 반환, 보통 집합함수와 함께 사용(집합 함수는 null 미포함하기 때문)
- 예시 : 사용자 이름이 '박지성'이면 null을 반환 그렇지 않으면 m.username을 반환할 때
- select NULLIF(m.username, '박지성') from Member m
다형성 쿼리
JPQL로 부모 엔티티를 조회하면 자식 엔티티도 함께 조회한다.
// 부모
@Entity
@Inheritance (strategy = InheritanceType. SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {...}
// 자식 (Book, Album, Movie)
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
}
TYPE
TYPE은 엔티티 상속 구조에서 조회 대상을 특정 자식 타입으로 한정할 때 사용한다.
예 : Item중 Album과 Book을 조회할 때
//JPQL
select i from Item i
where type(i) IN (Album, Book)
//SQL
SELECT i FROM Item i
WHERE i.DTYPE in (’A', 'B’)
TREAT
TREAT는 JPA 2.1에 추가된 기능으로 타입 캐스팅과 비슷하다.
상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용한다.
예 : Item i를 자식 타입인 Book으로 다룸으로써 author에 접근 가능하다.
//JPQL
select i from Item i where treat(i as Book).author = 'kim'
//SQL
select i.* from Item i
where
i.DTYPE='B' and i.author='kim'
기타 정리
- enum은 = 비교 연산만 지원한다.
- 임베디드 타입은 비교를 지원하지 않는다.
NULL 정의
- 조건을 만족하는 데이터가 하나도 없을 때,
- 알 수 없는 값일 때,
- Null == Null은 알 수 없는 값이다.
- Null is Null 은 참이다.
이밖에도 쿼리 파라미터 바인딩에서 값 대신 엔티티를 직접 넣어 줄 수 있다. 이때는 해당 엔티티의 기본 키를 가지고 작동한다.
Named 쿼리 : 정적 쿼리
- 동적 쿼리 : em.createQuery("select ...") 처럼 JPQL을 문자로 완성해서 직접 넘기는 것, 런타임에 특정 조건에 따라 JPQL을 동적으로 구성할 수 있다.
- 정적 쿼리 : 미리 정의한 쿼리에 이름을 부여해서 필요할 때 사용하는 것, Named 쿼리라 부르며 한 번 정의하면 변경할 수 없는 정적 쿼리다.
Named 쿼리를 사용함으로써 애플리케이션 로딩 시점에 문법 체크 및 파싱을 미리 해두기 때문에, 오류 체크나 파싱된 결과를 재사용하는데 성능상 지점이 있다. 또한 변하지 않는 정적 쿼리이기 때문에 조회 성능 최적화에도 도움이 된다.
사용 방식은 @NamedQuery 어노테이션을 사용해서 자바 코드에 작성하거나 XML 문서에 작성할 수 있다.
Named 쿼리를 어노테이션에 정의
이름 그대로 쿼리에 이름을 부여해서 사용한다.
em.createNamedQuery() 메서드를 사용한다.
또한 name에 엔티티명을 붙여주는데 이는 다른 엔티티에서도 동일한 Named 쿼리명을 사용할 수 있어 관리 상 용이하게 하기 위해서이다.
@Entity
@NamedQuery(
name = "Member.findByUsername”,
query="select m from Member m where m.username = :username”)
public class Member {
...
}
// 사용
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "손흥민")
.getResultList();
만약 하나의 엔티티에 2개 이상의 Named 쿼리를 작성하려면 @NamedQueries 어노테이션을 사용한다.
@NamedQueries({
@NamedQuery(
name = "Member.findByUsername",
query= "select m from Member m where m.username = :username"),
@NamedQuery(
name = "Member.count",
query= "select count(m) from Member m")
})
JPQL에 관련한 내용이지만 거의 대부분은 SQL에 대한 내용과 유사하다고 볼 수 있다.
따라서 SQL 공부를 열심히 하면 JPQL이나 뒤에서 공부할 Criteria 등에서도 유용할 거라 생각한다.
결론 : SQL 공부 파이팅!
'Language > Java' 카테고리의 다른 글
[Java] Thread-Safe 동기화 방식(Lock)과 Thread-Safe Queue (0) | 2025.03.21 |
---|---|
[JAVA] Call by Value VS Call by Reference (0) | 2024.07.19 |
[이펙티브자바] 아이템 29, 30, 31 요약 정리 (1) | 2024.06.09 |
[이펙티브자바] 아이템 26, 27, 28 요약 정리 (0) | 2024.06.07 |
[이펙티브자바] 아이템 25 요약 정리 (0) | 2024.06.07 |