객체지향 쿼리 언어
JPA는 다양한 쿼리 방법을 지원
- JPQL
- JPA Criteria
- QueryDSL
- 네이티브 SQL
- JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용
JPQL 소개
가장 단순한 조회 방법
- EntityManeger.find()
- 객체 그래프 탐색
JPA를 사용하면 엔티티 객체를 중심으로 개발
문제는 검색 쿼리
검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
모든 DB 데이터를 객체로 변환해서 검색하는 것을 불가능
애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
JPQL은 엔티티 객체를 대상으로 쿼리
SQL은 데이터베이스 테이블을 대상으로 쿼리
Criteria 소개
문자가 아닌 자바코드로 JPQL을 작성할 수 있음
JPQL 빌더 역할
JPA 공식 기능
단점 : 너무 복잡하고 실용성이 없다
Criteria 대신 QueryDSL 사용 권장
QueryDSL 소개
문자가 아닌 자바코드로 JPQL을 작성할 수 있음
JPQL 빌더 역할
컴파일 시점에 문법 오류를 찾을 수 있음
동적쿼리 작성 편리함
단순하고 쉬움
네이티브 SQL 소개
JPA가 제공하는 SQL을 직접 사용하는 기능
JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
JDBC 직접 사용, SpringJdbcTemplate
JPA를 사용하면서 JDBC커넥션을 직접 사용하거나, 스프링 JdbcTemplate 함께 사용 가능
단 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요
JPQL
JPQL은 객체지향 쿼리 언어이다. 엔티티 객체를 대상으로 쿼리한다
JPQL은 SQL을 추상화해서 특정데이터베이스 SQL에 의존하지 않는다.
JPQL은 결국 SQL로 변환된다.
JPQL 문법
엔티티와 속성은 대소문자 구분O
JPQL 키워드는 대소문자 구분X
엔티티 이름 사용, 테이블 이름이 아님
별칭은 필수
집합과 정렬
select
count(m) // 회원수
sum(m.age) // 나이 합
AVG(m.age) // 평균 나이
MAX(m.age) // 최대 나이
MIN(m.age) // 최소 나이
from Member m
TypeQuery, Query
TypeQuery : 반환 타입이 명확할 때 사용
Query :반환 타입이 명확하지 않을 때 사용
결과 조회 API
query.getResultList() : 결과가 하나 이상일 때, 리스트 반환, 결과가 없으면 빈 리스트 반환
query.getSingleResult() : 결과가 정확히 하나, 단일 객체 반환
프로젝션
SELECT 절에 조회할 대상을 지정하는 것
프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입
SELECT m FROM Member m -> 엔티티 프로젝션
SELECT m.team FROM Member m -> 엔티티 프로젝션
SELECT m.address FROM Member m -> 임베디드 타입 프로젝션
SELECT m.username, m.age FROM Member m -> 스칼라 타입 프로젝션
DISTINCT로 중복 제거
프로젝션 - 여러 값 조회
SELECT m.username, m.age FROM Member m
- Query 타입으로 조회
- Object[] 타입으로 조회
- new 명령어로 조회
- 단순 값을 DTO로 바로 조회 SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
- 패키지명을포함한전체클래스명입력
- 순서와 타입이 일치하는 생성자 필요
페이징 API
JPA는 페이징을 두 API로 추상화
setFirstResult() : 조회 시작 위치
setMaxResult() : 조회할 데이터 수
조인
내부 조인
- 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.useranme = t.name
조인 ON절
ON절을 활용한 조인
- 조인 대상 필터링
- 연관관계 없는 엔티티 외부 조인
조인 대상 필터링
회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
JPQL:
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = ‘A’
SQL:
SELECT m., t. FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name=’A’
연관관계 없는 엔티티 외부 조인
예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
JPQL:
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
SQL:
SELECT m., t. FROM Member m LEFT JOIN Team t ON m.username = t.name
서브쿼리
나이가 평균보다 많은 회원
select m from Member m where m.age > (select avg(m2.age) from Member m2)
한건이라도주문한고객
select m from Member m where (select count(o) from Order o where m = o.member) > 0
서브쿼리 한계
JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
SELECT 절도 가능
FROM절의 서브쿼리는 현재 JPQL에서 불가능
조건식 - CASE
COALESCE: 하나씩 조회해서 null이 아니면 반환
사용자 이름이 없으면 이름 없는 회원을 반환
- select coalesce(m.username,’이름 없는 회원’) from Member m
NULLIF: 두 값이 같으면 null 반환, 다르면 첫번째 값 반환
사용자 이름이 ‘관리자’면 null을 반환하고 나머지는 본인의 이름을 반환
- select NULLIF(m.username, ‘관리자’) from Member m
JPQL 경로 표현식
상태 필드: 단순히 값을 저장하기 위한 필드 (ex: m.username)
연관 필드: 연관관계를 위한 필드 단일 값 연관 필드:
-
@ManyToOne, @OneToOne, 대상이 엔티티(ex: m.team) 컬렉션 값 연관 필드:
-
@OneToMany, @ManyToMany, 대상이 컬렉션(ex: m.orders)
경로 표현식 특징
상태 필드: 경로 탐색의 끝, 탐색X
-
JPQL: select m.username, m.age from Member m
-
SQL: select m.username, m.age from Member m
단일 값 연관 경로: 묵시적 내부 조인 발생, 탐색O
-
JPQL: select o.member from Order o
-
SQL: select m.* from Orders o inner join Member m on o.member_id = m.id
컬렉션 값 연관 경로: 묵시적 내부 조인 발생, 탐색X
- FROM 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통 해 탐색 가능
명시적 조인, 묵시적 조인
명시적 조인: join 키워드 직접 사용
- select m from Member m join m.team t
묵시적 조인: 경로 표현식에 의해 묵시적으로 SQL 조인 발생 (내부 조인만 가능)
- select m.team from Member m
묵시적 조인 주의사항
- 항상 내부 조인
- 컬렉션은 경로 탐색의 끝, 명시적 조인을 통해 별칭을 얻어야함
- 경로 탐색은 주로 SELECT, WHERE절에서 사용하지만, 묵시적 조인으로 인해 SQL의 FROM절에 영향을 줌
JPQL - 페치 조인
페치 조인(fetch join)
SQL 조인 종류X
JPQL에서 성능 최적화를 위해 제공하는 기능
연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
join fetch 명령어 사용
페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로 |
엔티티 페치 조인
회원을 조회하면서 연관된 팀도 함께 조회(SQL 한 번에)
SQL을 보면 회원 뿐만 아니라 팀(T.*)도 함께 SELECT
[JPQL]
- select m from Member m join fetch m.team
[SQL]
-SELECT M., T. FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID=T.ID
컬렉션 페치 조인
일대다 관계, 컬렉션 페치 조인
[JPQL]
select t from Team t join fetch t.members where t.name = ‘팀A’
[SQL]
SELECT T., M. FROM TEAM T INNER JOIN MEMBER M ON T.ID=M.TEAM_ID WHERE T.NAME = ‘팀A’
페치 조인과 DISTINCT
SQL의 DISTINCT는 중복된 결과를 제거하는 명령
JPQL의 DISTINCT 2가지 기능 제공
- SQL에 DISTINCT를 추가
- 애플리케이션에서 엔티티 중복 제거
페치 조인의 특징과 한계
페치 조인 대상에는 별칭을 줄 수 없다.
하이버네이트는 가능, 가급적 사용X
둘 이상의 컬렉션은 페치 조인 할 수 없다.
컬렉션을 페치 조인하면 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.
일대일다대일같은단일값연관필드들은페치조인해도페이징가능
하이버네이트는 경고 로그를 남기고 메모리에서 페이징(매우 위험)
연관된 엔티티들을 SQL 한 번으로 조회 - 성능 최적화
엔티티에 직접 적용하는 글로벌 로딩 전략보다 우선함
- @OneToMany(fetch = FetchType.LAZY) //글로벌 로딩 전략
실무에서 글로벌 로딩 전략은 모두 지연 로딩
최적화가 필요한 곳은 페치 조인 적용
### 페치 조인 - 정리 모든 것을 페치 조인으로 해결할 수 는 없음
페치조인은객체그래프를유지할때사용하면효과적
여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 하면, 페치 조인 보다는 일반 조인을 사용하고 필요 한 데이터들만 조회해서 DTO로 반환하는 것이 효과적
JPQL - 다형성 쿼리
조회 대상을 특정 자식으로 한정
예) item중 BOok, Movie를 조회하라
[JPQL]
select i from Item i where type(i) IN (Book, Movie)
[SQL]
select i from i where i.DTYPE in (‘B’, ‘M’)
JPQL - 엔티티 직접 사용
엔티티 직접 사용 - 기본 키 값
JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기 본 키 값을 사용
[JPQL]
select count(m.id) from Member m //엔티티의 아이디를 사용
select count(m) from Member m //엔티티를 직접 사용
select count(m.id) as cnt from Member m
JPQL - Named 쿼리
Named 쿼리 - 정적 쿼리
미리 정의해서 이름을 부여해두고 사용하는 JPQL
정적쿼리
어노테이션, XML에 정의
애플리케이션 로딩 시점에 초기화 후 재사용
애플리케이션 로딩 시점에
쿼리를 검증
JPQL - 벌크 연산
재고가 10개 미만인 모든 상품의 가격을 10% 상승하려면?
JPA변경감지기능으로실행하려면너무 많은SQL실행
- 재고가 10개 미만인 상품을 리스트로 조회한다
- 상품 엔티티의 가격을 10% 증가
- 트랜잭션 커밋 시점에 변경감지 동작
변경된 데이터가 100건이라면 100번의 UPDATE SQL 실행
주의
벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
- 벌크 연산을 먼저 실행
- 벌크 연산 수행 후 영속성 컨텍스트 초기화
댓글남기기