JPQL(Java Persistence Query Language)
JPQL은 SQL을 추상화하여 특정 데이터베이스 SQL에 의존적이지 않은 객체지향 쿼리 언어이다.
테이블을 대상으로 쿼리를 하는 것이 아닌 객체(엔티티)를 대상으로 쿼리를 하기에 객체지향 쿼리 언어라고 불린다.
JPQL은 결국 SQL로 변환되어 데이터베이스에 전달된다.
ex)
select m from Member as m where m.age > 20
- 엔티티와 속성은 대소문자를 구분한다. (Member, age)
- JPQL 키워드는 대소문자를 구분하지 않는다. (SELECT, select 모두 가능)
- 테이블이 아닌 엔티티의 이름을 사용한다. (Member)
- 별칭은 필수다. (m) (as는 생략 가능)
EXISTS, IN, AND, OR, NOT, =, >, >=, <, <=, <>, BETWEEN, LIKE, IS NULL 등의 문법을 지원합니다.
IN
selet m from Member m where m.age in(10,15,20)
회원의 나이가 10,15,20인 회원을 찾아라
파라미터 바인딩
select m from Member m where m.username =:username
query.setParameter("username",원하는회원이름);
반환 타입
TypeQuery<Member> query = em.createQuery("select m from Member m", Member.class);
TypeQuery -> 반환 타입이 명확할 때 사용한다.
Query query = em.createQuery("select m.username, m.age from Member m");
Query -> 반환 타입이 명확하지 않을 때 사용한다.
결과 조회
qeury.getResultList(): 결과가 하나 이상일 때, 리스트를 반환한다. (결과가 없다면 빈 리스트를 반환한다.)
query.getSingleResult() : 결과가 정확히 하나일때 사용한다. 단일 객체를 반환한다.
(결과가 없으면 NoResultException, 결과가 둘 이상이면 NonUniqueResultException이 발생한다.)
프로젝션
프로젝션이란 SELECT 절에서 조회할 대상을 지정하는 것이다.
JPQL에서 프로젝션의 대상
- 엔티티
- 임베디드 타입
- 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
다음과 같은 코드가 있다.
@Data
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@Embedded
pricate Address adress;
@ManyToOne(fetch = FetchType.Lazy)
@JoinColumn(name="team_id")
private Team team;
}
@Embeddable
@Data
public class Address{
private String city;
private String street;
private String zipcode;
}
@Data
@Entity
public class Team{
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArratList<>();
}
엔티티 프로젝션
select m FROM Member m
select m.team from Member m
inner join이 발생
join의 경우 명확하게 작성하는 것이 좋다.
실제 어디서 조인쿼리가 나갔는지 찾기 어려워질 수 있기 때문이다.
select t from Member m join m.team t
임베디드 타입
select m.address from Member m
스칼라 타입
select m.username, m.age from Member m
스칼라 타입 프로젝션의 결과 조회 방법
- Query 타입으로 조회
- Object[] 타입으로 조회 (List <Object []>)
- new 명령어로 조회 (단순 값을 DTO로 바로 조회하는 방법이다.
- SELECT new jpabook.dto.UserDTO(m.name, m.age) FROM Member m
- DTO 클래스에는 패키지 명을 포함한 전체 클래스 명을 입력해야 한다.
- 순서와 타입이 일치하는 생성자가 필요하다.
DISTINCT로 중복을 제거할 수 있다.
페이징
JPA는 페이징을 다음 두 개의 API로 추상화했다.
setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
setMaxResults(int maxResult) : 조회할 데이터 수
DataBase에 5명 이상의 멤버객체를 저장 후 다음과 같은 코드를 실행시켰다.
setFirstResult(int startPosition)의 값을 0으로 정하면 select문 마지막에 offset이 없다.
하지만 0이 아닌 다른 값을 넣어주면 다음과 같은 결과가 나온다.
조인
join(조인) 또는 결합 구문은 한 데이터베이스 내의내의 여러 테이블의 레코드를 조합하여 하나의 열로 표현한 것이다.
다른 말로 조인이란 여러 테이블에 있는 정보중 사용자가 필요한 정보만 가져와 가상의 테이블처럼 만들어서 결과를 보여주는 것으로 n개의 테이블을 조합하여 하나의 열로 표현한 것이다.
내부 조인
키 값이 있는 테이블의 칼럼 값을 비교해서 맞는 값을 가져오는 것이다.
한 마디로 서로 관련된 내용을 검색해 가져오는 방법이다.
두 테이블의 교집합이라고 생각하면 쉽다.
select m from Member m (inner) join m.team t //(내부 조인의 경우 inner생략가능)
외부 조인
외부 조인 은 여러 테이블 중 한 테이블에만 데이터가 있고 다른 쪽에는 없는 경우,
데이터가 있는 테이블의 내용을 전부 출력하는 방법이다
select m from Member m left (outer) join m.team t //(외부 조인의 경우 left는 필수, outer는 생략가능)
컬렉션 조인
select t from Team t left join t.members m
세타 조인
CROSS JOIN은 두 테이블의 모든 조합을 받아온다.
select m from Member m, Team t where m.username = t.name
세타 조인은 Cross Join이 발생한다 (카테시안 곱)
또한 기본적으로는 내부 조인만 가능하며, ON을 쓰면 외부 조인도 가능해진다.
JOIN ON 절
내부 조인의 on절은 WHERE 절과 결과가 똑같으므로 보통 JOIN ON절은 외부 조인에서만 사용한다
- 조인 대상 필터링
- 연관관계없는 엔티티 외부 조인 가능(하이버네이트 5.1부터 지원)
select m, t from Member m left join m.team t on t.name ='A'
회원과 팀을 조인하는데, 팁 이름이 A인 팀만 조인
select m, t from Member m left join Team t on m.username = t.name
회원의 이름과 팁의 이름이 같은 대상 외부 조인 (연관관계없는 엔티티 외부 조인)
서브 쿼리
JPQL도 SQL처럼 서브 쿼리를 지원한다.
그러나 몇 가지 제약이 있는데, 서브쿼리를 WHERE, HAVING 절에서만 사용할 수 있고 SELECT, FROM 절에서는 사용할 수 없다.
(그러나 하이버네이트에서는 SELECT절의 서브 쿼리도 허용한다.)
나이가 평균보다 많은 회원 찾기
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
서브 쿼리 함수
- [NOT] EXISTS (서브쿼리) : 서브쿼리에 결과가 존재하면(존재하지 않으면) 참
- ALL (서브쿼리): 결과가 모두 만족하면 참
- ANY (서브쿼리): 조건을 하나라도 만족하면 참
- SOME (서브쿼리): ANY와 동일
- [NOT] IN (서브쿼리) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면(없으면) 참
EXIST
서브쿼리의 결과가 존재하면 참, NOT은 반대로 동작
select m from Member m where exists (select t from m.team t where t.name = '팀A')
ALL | ANY | SOME
비교 연산자와 같이 사용하며, ALL은 조건을 모두 만족하면 참, ANY 혹은 SOME은 조건을 하나라도 만족하면 참
전체 상품 각각의 제고보다 주문량이 많은 주문들
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)
조건식 - CASE
기본 CASE 식
select
case when m.age <= 10 then '학생'
when m.age >= 19 then '성인'
end
from Member m
단순 CASE 식
select
case t.name
when 'A' then '팀A입니다'
when 'B' then '팀B입니다'
else '나머지 팀입니다'
end
from Team t
COALESCE : 하나씩 조회해서 null이 아니면 반환
사용자 이름이 없으면 이름 없는 회원을 반환
select coalesce(m.username, '이름 없는 회원') from Member m
NULLIF : 두 값이 같으면 NULL, 다르면 첫 번째 값 반환
사용자 이름이 '관리자'면 null 반환, 나머지는 자기 자신의 이름 반환
select NULLIF(m.username, '관리자') from Member m
JPQL 집합과 정렬 등
select
COUNT(m), //전체 수
SUM(m.age), //나이의 합
AVG(m.age), // 나이의 평균
MAX(m.age),// 나이의 최댓값
MIN(m.age) // 나이의 최솟값
from Member m
GROUP BY, HAVING, ORDER BY 모두 가능합니다.
경로 표현식
.(점)을 찍어 객체 그래프를 탐색하는 것을 의미한다.
탐색할 수 있는 대상은 다음과 같다
- 상태 값 필드 경로(m.age) : 경로 탐색의 끝입니다. 더는 탐색할 수 없다.
- 단일 값 연관 경로(m.team) : 묵시적으로 내부 조인이 일어납니다. 계속 탐색할 수 있다.
- 컬렉션 값 연관 경로(m.orders o) : 묵시적으로 내부 조인이 일어납니다. 더는 탐색할 수 없다.
- 단 FROM 절에서 join을 통해 별칭을 얻으면 별칭으로 탐색을 수행할 수 있다. (t.memebers m)
묵시적 조인을 사용하지 말고 명시적 조인을 사용하는 것을 권장함. (명시적으로 JOIN 키워드를 사용)
다형성 쿼리
JPQL로 부모 엔티티를 조회하면 그 자식 엔티티도 함께 조회된다.
다형성 쿼리는 조회 대상을 특정 자식으로 한정하기 위해 사용하는 방법이다.
TYPE
Item 중에 Book, Movie를 조회하라.
select i from Item i where type(i) IN (Book, Movie)
TREAT
자바의 타입 캐스팅과 비슷하다.
상속 구조에서 부모를 특정 자식의 타입으로 다룰 때 사용한다
select i form Item i where treat (i as Book).author = 'name'
'JPA' 카테고리의 다른 글
QueryDSL (0) | 2023.01.15 |
---|---|
페치 조인 (2) | 2023.01.10 |
JPA의 데이터 타입 (3) | 2023.01.07 |
프록시 & 지연로딩 (0) | 2023.01.07 |
엔티티 매핑 (0) | 2023.01.06 |