객체지향쿼리언어 JPQL에 대해 알아보자!

객체지향쿼리언어 (JPQL)

JPA의 쿼리 방법 지원

  • JPQL
  • JPA Criteria
  • QueryDSL
  • 네이티브SQL (그냥 sql 쿼리 날리는 거)
  • JDBC API 직접 사용, MyBatis, Spring JDBC Template

이 중에서도 JPQL 과 QueryDSL의 조합으로 사용하는 것이 좋아!!


👏👏 주의사항 👏👏

MyBatis나 JDBC Template 등을 이용할 때에 주의사항이 있어. 영속성 컨텍스트에 들어가있는 데이터는 flush 될 때 실제 db에 반영이 되지. 보통 jpa에서 지원하는 쿼리를 사용하면 flush를 한 후 반영된 db에서 가지고 오지만 그것이 아닌 기술을 활용할 때에는 직접 flush를 해주어 DB상태를 업데이트 후 데이터를 처리하는 작업을 진행해야해!!

그럼 JPQL에 대해 자세히 알아보자



JPQL(Java Persistence Query Language)

JPQL 기본개념

  • JPQL은 객체지향 쿼리 언어야!! 따라서 테이블을 대상으로 하는 것이 아닌 엔티티 객체를 대상으로 쿼리를 날려
  • JPQL은 SQL을 추상화하기에 특정데이터베이스 SQL에 의존하지 않아!!
  • JPQL은 결국은 내부적으로 SQL로 변환이 되어져. 당연한 것이겠지

JPQL 문법

SELECT

  • select절 from절 [where_절] [groupby_절] [having_절] [orderby_절]
  • ex) select m from Member as m where m.age > 18 (as는 생략가능)

UPDATE, DELETE

  • update절 [where절]
  • delete절 [where절]

문법사항

  • 엔티티와 속성은 대소문자 구분해 (Member, age 등등)
  • 하지만 JPQL 키워드는 대소문자 구분안해 (SELECT를 하던 select를 하던 구분 안한다 이말이야)
  • 테이블 이름이 아닌 엔티티 이름을 사용해! 너무 당연하지.
  • 그리고 별칭은 필수야!!

집계함수

기본적으로 집계함수는 다 지원이 돼!

1
2
3
4
5
6
7
select
COUNT(m), //회원수
SUM(m.age), //나이 합
AVG(m.age), //평균 나이
MAX(m.age), //최대 나이
MIN(m.age) //최소 나이
from Member m

GROUP BY, HAVING, ORDER BY 모두 사용 가능해!!


TypeQuery와 Query

  • TypeQuery : 반환 타입이 명확할 때 사용하는 놈이야
  • Query : 반환 타입이 명확하지 않을 때 사용하는 놈이야
1
2
3
4
5
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m", Member.class);

Query query =
em.createQuery("SELECT m.username, m.age from Member m");

이렇게 말이야!

타입이 정해지지 않은 경우에도 TypeQuery를 쓸 수 있어!! 어떻게?? Object.class를 이용해서 받으면 되지!!



결과 조회 API

  • query.getResultList() : 결과가 하나 이상일 때, 리스트 반환해줘!! 만약 없다면 빈 리스트 반환해줘
  • query.getSingleResult() : 결과가 정확히 하나일 때 사용해!! 정확히 하나!! 만약 결과가 없거나 둘 이상이면 각각 NoResultException, NonUniqueResultException 예외 던져



파라미터 바인딩 (이름 기준, 위치 기준)

파라미터 바인딩에는 크게 이름 기준과 위치 기준이 있는데 그냥 이름 기준을 사용해!! 아무래도 위치 기준은 이후 수정이 됬을 때 오류를 범하거나 귀찮아질 확률이 크닝께!

1
2
SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam);



프로젝션

프로젝션은 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()로 중복제거 가능해!! sql 처럼 말야 )


프로젝션에서 여러 값 조회할 때

  • query 타입으로 조회 ( 위에서 봤던 것 처럼 타입이 정해지지 않은 결과에 대해 사용 )
  • Object[] 타입으로 조회 ( TypedQuery에서 Object.class로 타입을 정하고 Object[] 로 받아서 결과를 확인할 수 있어 )
  • new 명령어로 조회!!
1
2
3
4
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM
Member m
//이런식으로 가능하지만 정의한 DTO의 패키지 명을 포함한 전체 클래스 명 입력이 필요하며
//순서와 타입이 일치하는 생성자가 필요해

(Spring Data Jpa + QueryDSL 조합으로 가게되면 훨씬 편하게 가능해)



페이징 API

JPA는 페이징을 아래의 두 API로 추상화해서 제공해줘!!

  1. setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
  2. setMaxResults(int maxResult) : 조회할 데이터 수
1
2
3
4
5
String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(10)
.setMaxResults(20)
.getResultList();

이런식으루 말야!!



조인

  • 내부 조인 : 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



조인 ON 절

  1. 조인 대상 필터링
  2. 연관곤계 없는 에티티 외부 조인

조인 대상 필터링

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

이런식으로 사용할 수 있어.



서브 쿼리

select m from Member m
where m.age > (select avg(m2.age) from Member m2)

이런식으로 사용할 수 있어!!

이때 함께 사용할 수 있는 함수들이 있는데 sql에서 지원하는 건 거진 된다고 생각하면 돼!!

  • [NOT[ EXISTS (subquery) : 서브 쿼리에 결과가 존재하면 참
  • ALL (subquery) : 모두 만족하면 참
  • ANY | SOME (subquery) : 조건을 하나라도 만족하면 참
  • [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

ex)

1
2
3
4
5
6
7
8
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)

👏👏👏 주의 사항 👏👏👏

FROM 절의 서브 쿼리는 JPQL에서 불가능해!!


조건식 - CASE 식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//기본 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 '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t

IF와 SWITCH의 차이와 비슷한 느낌이라고 생각하면 될 것 같애!!

  • COALESCE : 하나씩 조회해서 NULL이 아니면 반환 (예를들어 이름이 없는 사람들에 대해 ‘이름 없는 회원’ 으로 조회하고 싶을 때 사용하면 된다)
  • NULLIF : 두 값이 같으면 NULL 반환, 다르면 첫번째 값 반환!! (특정 값에 해당하는 데이터를 가려야할 때)
1
2
3
select coalesce(m.username,'이름 없는 회원') from Member m

select NULLIF(m.username, '관리자') from Member m



JPQL 기본함수

  • CONCAT - 문자열 이어 붙일 때
  • SUBSTRING - 특정 위치 문자열 추출할 때
  • TRIM - 공백 제거
  • LOWER, UPPER - 대소문자화
  • LENGTH - 문자열길이
  • LOCATE - 특정 문자열의 시자 위치
  • ABS, SQRT, MOD
  • SIZE - 특정 엔티티와 연관관계에 있는 엔티티들을 가리키는 LIST형 참조변수의 크기
  • 사용자 정의 함수도 사용할 수 있어. 쓸일 있을 때 찾아서 공부해서 쓰길..









https://www.inflearn.com/course/ORM-JPA-Basic