반응형
250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- SQL 튜닝
- 그룹함수
- 값 타입
- 정렬
- JPA
- DTO
- @MappedSuperclass
- Spring
- 페치조인
- INDEX SCAN
- 자바의 정석
- 컬렉션 조회 최적화
- querydsl
- TCP/IP
- 스프링 데이터 JPA
- fetch join
- 스프링 컨테이너
- 엔티티
- Spring data JPA
- 페이징
- 친절한 SQL 튜닝
- 스프링
- 오라클
- index
- SQL
- JPQL
- 데이터모델링
- 데이터베이스
- 서브쿼리
- 성능최적화
Archives
- Today
- Total
nu_s
[JPA] JPQL 🐶 본문
728x90
반응형
JPQL(Java Persistence Query Language)
- JPA를 사용하면 엔티티 객체를 중심으로 개발한다.
- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색한다.
- 모든 데이터를 객체로 변환해서 검색하는 것은 불가능하기 때문에, 필요한 데이터만 불러오려면 검색 조건이 포함 된 SQL이 필요하다.
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
- SQL과 문법이 유사하다.
- JPQL은 엔티티 객체를 대상으로 쿼리를 만든다.
JPQL 예시
//검색
List<Member> result = em.createQuery("select m from Member m where m.name like '%hello%'", Member.class)
.getResultList();
- 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리이다.
- SQL을 추강화해서 특정 데이터베이스 SQL에 의존하지 않는다.
- JPQL은 결국 SQL로 변환된다.
1. JPQL - 기본 문법과 기능
- select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자를 구분한다. (Member, age)
- JPQL 키워드는 대소문자를 구분하지 않는다. (select, from, where)
- 테이블 이름이 아닌 엔티티 이름을 사용한다.
- 별칭은 필수이다.
1-1. TypeQuery, Query
- TypeQuery : 반환값이 명확할 때 사용한다.
- Query : 반환값이 명확하지 않을 때 사용한다.
TypeQuery<Member> query = em.createQuery("select m from Member m", Member.class);
Query query = em.createQuery("select m.username, m.age from Member m");
1-2. 결과 조회 API
- query.getResultList()
- 결과가 하나 이상일 때, 리스트로 반환
- 결과가 없으면 빈 리스트 반환
- query.getSingleResult()
- 결과가 정확히 하나일 때, 단일 객체 반환
- 결과가 없으면 javax.persistence.NoResultException
- 둘 이상이면 javax.persistence.NonUniqueResultException
1-3. 파라미터 바인딩
em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", usernameParam)
1-4. 프로젝션
- SELECT 절에 조회할 대상을 지정하는 것이다.
- 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(기본 데이터 타입)
여러 값 조회
- SELECT m.username, m.age FROM Member m
- Query 타입으로 조회
- Object[] 타입으로 조회
- new 명령어로 조회
- 단순 값을 DTO로 바로 조회
- SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
- 패키지명을 포함한 전체 클래스명 입력
- 순서와 타입이 일치하는 생성자가 필요하다.
1-5. 페이징 API
- JPA는 페이징을 다음 두 API로 추상화한다.
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
- setMaxResult(int MaxResult) : 조회할 데이터 수
1-6. 조인
- 내부 조인
SELECT m FROM Member m [INNER] JOIN m.team t - 외부 조인
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t - 세타 조인
SELECT count(m) FROM Member m, Team t WHERE m.username = t.name
1-6-1. ON 절
- ON절을 활용한 조인 (JPA 2.1부터 지원)
- 조인 대상 필터링
- 연관관계 없는 엔티티 외부 조인 (하이버네이트 5.1부터)
--1.조인 대상 필터링
--회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
SELECT m, t FROM Member m LEFT JOIN m.team t ON t.name = 'A'
--2.연관관계 없는 엔티티 외부 조인
--회원의 이름과 팀의 이름이 같은 대상 외부 조인
SELECT m, t FROM Member m LEFT JOIN Team t ON m.username = t.name
1-7. 서브쿼리
1-7-1. 서브쿼리 지원 함수
- [NOT] EXISTS (subquery) : 서브쿼리에 결과가 존재하면 참
- {ALL | ANY | SOME} (subquery)
- ALL : 모두 만족하면 참
- ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참
- [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
--팀A 소속인 회원
select m from Member m where exists (select t from m.team t where t.name = '팀A')
--전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)
--어떤 팀이든 팀에 소속된 회원
select m from Member m where m.team = ANY (select t from Team t)
1-7-2. 서브쿼리의 한계
- JPA는 WHERE, HAVING절에서만 서브쿼리를 사용할 수 있다.
- SELECT절도 가능하다.(하이버네이트에서 지원)
- FROM절의 서브쿼리는 하이버네이트6부터 지원한다.
1-8. 조건식
1-8-1. CASE식
--기본 CASE 식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
--단순 CASE 식
select
case t.name
when '팀A' then '인센티브110%'
when '팀A' then '인센티브120%'
else '인센티브 105%'
end
from Team t
1-8-2. COALESCE
- 하나씩 조회해서 null이 아니면 반환
--사용자 이름이 없으면 이름 없는 회원 반환
select coalesce(m.username, '이름 없는 회원') from Member m
1-8-3. NULLIF
- 두 값이 같으면 null반환, 다르면 첫번째 값 반환
--사용자 이름이 '관리자'면 null반환, 나머지는 본인의 이름을 반환
select NULLIF(m.username, '관리자') from Member m
1-8-4. 그 외의 기본 함수
- CONCAT : 문자열 더하기
- SUBSTRING : 문자열 자르기
- TRIM : 공백 제거
- LOWER, UPPER : 대문자, 소문자 변환
- LENGTH : 길이 반환
- LOCATE : 찾는 문자열의 위치 반환
- ABS, SQRT, MOD : 절댓값, 제곱근, 나머지
- SIZE, INDEX : 크기, 인덱스
2. 경로 표현식
2-1. 경로 표현식
- .(점)을 찍어 객체 그래프를 탐색하는 것
- 용어 정리
- 상태 필드(state field) : 단순히 값을 저장하기 위한 필드
- 연관 필드(association field) : 연관관계를 위한 필드
- 단일 값 연관 필드 : @ManyToOne, @OneToOne, 대상이 엔티티
- 컬렉션 값 연관 필드 : @OneToMany, @ManyToMany, 대상이 컬렉션
select m.username -- 상태 필드
from Member m
join m.team t -- 단일 값 연관 필드
join m.orders o -- 컬렉션 값 연관 필드
where t.name = '팀A'
2-2. 경로표현식 특징
- 상태 필드 : 경로 탐색의 끝, 더 이상 탐색 불가
- 단일 값 연관 경로 : 묵시적 내부 조인(inner join)발생, 더 탐색 가능
- 컬렉션 값 연관 경로 : 묵시적 내부 조인 발생, 더 이상 탐색 X
- FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능하다.
2-3. 명시적 조인과 묵시적 조인
- 명시적 조인 : join 키워드를 직접 사용하는 것
- select m from Member m join m.team t
- 묵시적 조인 : 경로 표현식에 의해 묵시적으로 SQL 조인 발생 (내부 조인만 가능)
- select m.team from Member m
경로 탐색을 사용한 묵시적 조인 시 주의사항
- 항상 내부 조인
- 컬렉션은 경로 탐색의 끝, 명시적 조인을 통해 별칭을 얻어야 한다.
- 경로 탐색은 주로 SELECT, WHERE 절에서 사용하지만 묵시적 조인으로 인해 SQL의 FROM절에 영향을 줌
- 가급적 묵시적 조인 대신에 명시적 조인 사용
3. 페치 조인(fetch join)
3-1. 페치 조인
- SQL 조인 종류가 아니다.
- JPQL에서 성능 최적화를 위해 제공하는 기능이다.
- 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능이다.
- join fetch 명령어를 사용한다.
3-2. 엔티티 페치 조인
- 회원을 조회하면서 연관된 팀도 함께 조회한다.
- SQL을 보면 회원 뿐만 아니라 팀도 함께 SELECT 한다.
select m from Member m join fetch m.team
3-3. 컬렉션 페치 조인
- 일대다 관계, 컬렉션 페치 조인
select t from Team t join fetch t.members
- 컬렉션 페치 조인 사용 시 값이 중복될 수 있다.
3-4. 페치 조인과 DISTINCT
- DISTINCT가 추가로 애플리케이션에서 중복 제거를 시도한다.
- 같은 식별자를 가진 엔티티를 제거한다.
- 하이버네이트6 부터는 DISTINCT 명령어를 사용하지 않아도 자동 중복 제거가 된다.
3-5. 페치 조인과 일반 조인의 차이
- 일반 조인 실행 시 연관된 엔티티를 함께 조회하지 않는다.
- JPQL은 결과를 반환할 때 연관관계를 고려하지 않는다.
- 단지 SELECT 절에 저장한 엔티티만 조회한다.
- 페치 조인을 사용할 때만 연관된 엔티티도 함께 조회한다. (즉시 로딩)
- 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념이다.
3-6. 페치 조인의 특징과 한계
- 페치 조인 대상에는 별칭을 줄 수 없다.
- 하이버네이트는 가능하지만, 가급적 사용하지 않는다.
- 둘 이상의 컬렉션은 페치 조인 할 수 없다.
- 컬렉션을 페치 조인하면 페이징 API를 사용할 수 없다.
- 일대일, 다대일 같은 단일 값 연관 필드들은 페치 조인해도 페이징이 가능하다.
- 하이버네이트는 경고 로그를 남기고 메모리에서 페이징한다. (매우 위험)
- 연관된 엔티티들을 SQL 한 번으로 조회한다. - 성능 최적화
- 엔티티에 직접 적용하는 글로벌 로딩 전략보다 우선한다.
- @OneToMany(fetch = FetchType.LAZY) // 글로벌 로딩 전략
- 실무에서 글로벌 로딩 전략은 모두 지연 로딩이다.
- 최적화가 필요한 곳에 페치 조인을 적용한다.
정리
- 모든 것을 페치 조인으로 해결할 수 는 없다.
- 페치 조인은 객체 그래프를 유지할 때 사용하면 효과적이다.
- 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 하면, 페치 조인보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는 것이 효과적이다.
4. 다형성 쿼리
4-1. TYPE
- 조회 대상을 특정 자식으로 한정한다.
select i from Item i where type(i) IN (Book, Movie)
4-2. TREAT
- 자바의 타입 캐스팅과 유사하다.
- 상속 구조에서 부모 타입을 특정 자식 타입으로 다룰 때 사용한다.
select i from Item i where treat(i as Book).author = 'kim'
5. 엔티티 직접 사용
- JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용한다.
select count(m.id) from Member m --엔티티의 아이디를 사용
select count(m) from Member m --엔티티를 직접 사용
-- 둘 다 같다.
6. Named 쿼리
- 미리 정의해서 이름을 부여해두고 사용하는 JPQL이다.
- 정적 쿼리
- 어노테이션, XML에 정의할 수 있다.
- 애플리케이션 로딩 시점에 초기화 한 후 재사용한다.
- 애플리케이션 로딩 시점에 쿼리를 검증한다.
6-1. 어노테이션에 정의
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
public class Member {
...
}
List<Member> resultList =
em.createQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
6-2. XML에 정의
<entity-mappings ...>
<named-query name="Member.findByUsername">
<query><![CDATA[
... //쿼리가 들어온다.
]]></query>
</named-query>
<entity-mappings>
Named 쿼리 환경에 따른 설정
- XML이 항상 우선권을 가진다.
- 애플리케이션 운영 환경에 따라 다른 XML을 배포할 수 있다.
7. 벌크 연산
- 재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
- JPA 변경 감지 기능으로 실행하기엔 SQL이 너무 많이 실행된다.
- 재고가 10개 미만인 상품을 리스트로 조회한다.
- 상품 엔티티의 가격을 10% 증가한다.
- 트랜잭션 커밋 시점에 변경감지가 동작한다.
- 변경된 데이터가 100건이라면 100번의 UPDATE SQL이 실행된다.
int resultCount =
em.createQuery("
update Product p
set p.price = p.price * 1.1
where p.stockAmount < :stockAmount")
.setParameter("stockAmount", 10)
.executeUpdate();
- 쿼리 한 번으로 여러 테이블 로우를 변경한다.
- executeUpdate()의 결과는 영향받은 엔티티의 수를 반환한다.
- UPDATE, DELETE를 지원한다.
- INSERT(insert into .. select, 하이버네이트 지원)
주의
- 벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 실행한다.
- 제대로 사용하려면
- 벌크 연산을 먼저 실행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
출처 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
728x90
반응형
'JPA' 카테고리의 다른 글
[JPA 활용] 조회 성능 최적화 - 엔티티 직접 노출🐱 (0) | 2023.11.04 |
---|---|
[JPA 활용] API 개발 (포스트맨) 🐱 (0) | 2023.10.26 |
[JPA] 값 타입 컬렉션 🐶 (0) | 2023.10.25 |
[JPA] 값 타입과 불변 객체 🐶 (0) | 2023.10.25 |
[JPA] 값 타입 (기본 값 타입, 임베디드 타입) 🐶 (0) | 2023.10.25 |