1. 페이징 API
- setFirstResult(int startPosition) : 조회 시작 위치 (0 부터 시작)
- setMaxResults(int maxResult): 조회할 데이터 수
✅ 사용 예시
//페이징 API
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(10)
.setMaxResults(20)
.getResultList();
이렇게 페이징API를 쓰면 10번 인덱스부터 20개의 결과를 받을 수 있다.
JPQL을 사용하면 이렇게 페이징 처리가 편하다.
관계형 데이터베이스인 Oracle에서는 페이징처리하는 쿼리가 3depth까지 가니
가독성도 떨어지고 매번 ROWNUM으로 페이징 쿼리를 작성하는 것이 귀찮은 부분이기도 하다.
✅Oracle 페이징 SQL쿼리 (참고용)
SELECT *
FROM (
SELECT
a.*,
ROWNUM rnum
FROM (
SELECT
m.username,
m.age
FROM
Member m
ORDER BY
m.username
) a
WHERE ROWNUM <= :endRow
)
WHERE rnum > :startRow;
2. 조인
2-1) JPQL 조인 예시
--내부 조인
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
✔️ INNER, OUTER 생략 가능
✔️ 세타 조인
- 두 엔티티 간의 명시적인 관계 매핑이 없을 때 사용한다.
- 두 엔티티의 조합 중 where 조건에 만족하는 데이터만 결과로 반환한다.
2-2) 컬렉션 조인
한 엔티티가 다른 엔티티의 컬렉션을 가지고 있을 때 사용하는 조인이다.
일반적으로 @OneToMany , @ManyToMany 관계에서 사용된다.
✅'특정 팀과 그 팀에 속한 멤버 정보' 조회 예시
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
- team이 Member를 리스트로 가지고 있다.
✅JPQL 쿼리
select t, m from Team t join t.members m on t.name = :teamName
- 팀 이름이 (:teamName)인 팀에 속한 멤버(t.members)를 조회하는 쿼리다.
2-3) 연관관계 없는 엔티티 외부 조인 (하이버네이트 5.1이상 버전)
--연관관계 있는 엔티티 외부 조인
SELECT m, t FROM Member m LEFT JOIN m.team t on m.username = t.name
--연관관계 없는 엔티티 외부 조인
SELECT m, t FROM
Member m LEFT JOIN Team t on m.username = t.name
✔️연관관계가 있는 엔티티를 외부 조인 할 때는 m.team으로 가져온다. (m에 있는 team)
✔️연관관계가 없는 엔티티를 외부 조인 할 때는 Team 엔티티 자체를 가져온다.
✅두 쿼리를 실행 했을 때, 차이
--연관관계 있는 엔티티 외부조인 쿼리 실행
select
m1_0.id,
m1_0.age,
m1_0.TEAM_ID,
m1_0.username
from
Member m1_0
left join
Team t1_0
on t1_0.id=m1_0.TEAM_ID
and t1_0.name=m1_0.username
--연관관계 없는 엔티티 외부조인 쿼리 실행
select
m1_0.id,
m1_0.age,
m1_0.TEAM_ID,
m1_0.username
from
Member m1_0
left join
Team t1_0
on t1_0.name=m1_0.username
- 연관관계가 있는 엔티티를 외부조인 할때는 PK, FK를 조인하는 쿼리가 자동생성된다.
- 연관관계가 없는 엔티티를 외부조인 할 때는 PK, FK를 조인하지 않고 on절에 있는 조건만 실행된다.
3. 서브 쿼리
서브쿼리는 다른 쿼리의 결과를 조건이나 값으로 사용하는 쿼리다.
JPA 표준에서는 WHERE, HAVING 절에서 서브 쿼리를 사용할 수 있다.
하이버네이트 6이전 버전은 SELECT절에도 서브쿼리를 사용할 수 있고
하이버네이트 6이후 버전은 FROM 절에도 사용가능하다.
3-1) 서브 쿼리 지원 연산자
- [NOT] EXISTS : 서브쿼리에 결과가 존재하면 참 ( NOT EXISTS는 결과가 존재하지 않으면 참 )
- ALL : 서브 쿼리 결과와 비교하여 모두 만족하면 참
- ANY, SOME : 서브쿼리 결과와 비교하여 조건을 하나라도 만족하면 참
- IN : 서브 쿼리의 결과 중 하나라도 같은 것이 있으면 참
3-2) 서브 쿼리 예시
✅(EXISTS) 특정 조건을 만족하는 팀이 존재하는 멤버 찾기
SELECT m FROM Member m WHERE EXISTS (SELECT t FROM m.team t WHERE t.name = :teamName)
- 이 쿼리는 더 쉽게 표현하자면, teamName이름을 가진 team에 속한 멤버를 찾는 쿼리다.
- 각 Member에 대해 m.team을 기준으로 Team 엔티티를 조회하고 Team의 이름이 :teamName과 일치하는 경우에만 Member를 결과에 포함시킨다.
- 이 방식은 각 멤버에 대해 서브쿼리가 실행되어 특정 조건을 만족하는지 확인한다.
✅(IN) 특정 팀에 속한 모든 멤버 찾기
SELECT m FROM Member m WHERE m.team.id IN (SELECT t.id FROM Team t WHERE t.name = :teamName)
- 특정 팀이름을 가진 팀에 속한 모든 멤버를 찾는다.
- 첫번째 예시와 논리적으로 결과는 동일하지만 내부 처리 방식에 차이가 있다.
서브 쿼리를 먼저 실행해서 조건에 맞는 team의 id를 가져오고 그 id와 일치하는 멤버를 찾는다.
✅(ALL) 모든 제품의 재고량보다 주문량이 많은 주문 찾기
SELECT o FROM Order o WHERE o.orderAmount > ALL (SELECT p.stockAmount FROM Product p)
- 각 Order 인스턴스의 주문량이 모든 제품의 재고량보다 큰지 하나하나 비교한다.
- 주문량이 모든 재고량보다 큰 주문을 찾는다.
✅(ANY) 제품의 재고량보다 주문량이 많은 주문 찾기
SELECT o FROM Order o WHERE o.orderAmount > ANY (SELECT p.stockAmount FROM Product p)
- 주문량과 제품의 재고량과 비교하여 한 건이라도 큰 게 있는 주문을 찾는다.
✅(HAVING) 각 팀의 평균 연령이 특정 값 이상인 팀 찾기
SELECT t FROM Team t JOIN t.members m GROUP BY t.id HAVING AVG(m.age) > :age
- 멤버들의 나이 평균이 :age보다 큰 팀을 찾는다.
🤔WHERE절에 집계함수를 쓰면 안될까? (안됨)
SELECT t FROM Team t JOIN t.members m WHERE AVG(m.age) > :age --틀린 쿼리
HAVING절 예제를 보고 써본 쿼리다.
결론적으로 WHERE절에는 집계함수를 쓰면 안된다.
왜냐하면, WHERE절은 개별 행을 필터링하는 데에 사용되고, 집계 함수는 그룹화된 데이터를 기반으로 계산되기 때문에 WHERE절에 쓸 수 없다.
🤓이번 포스팅에서 fetch join을 다루지 않았는데 fetch join은 단독 포스팅으로 다룰 예정!
'인프런 김영한 강의 정리 > 자바 ORM 표준 JPA 프로그래밍 기본편' 카테고리의 다른 글
JPA 기본 | JPQL 문법(5) - 조건식 (0) | 2024.07.17 |
---|---|
JPA 기본 | JPQL 문법(4) - ENUM 타입,상속관계 엔티티 표현 (0) | 2024.07.17 |
JPA 기본 | JPQL 문법(2) - 프로젝션(SELECT) (0) | 2024.07.16 |
JPA 기본 | JPQL 기본 문법(1) 쿼리 API, 파라미터 바인딩 (0) | 2024.07.15 |
JPA 기본 | 객체지향 쿼리 언어 알아보기(JPQL, QueryDSL 등) (0) | 2024.07.15 |