객체지향쿼리언어 (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 | select |
GROUP BY, HAVING, ORDER BY 모두 사용 가능해!!
TypeQuery와 Query
- TypeQuery : 반환 타입이 명확할 때 사용하는 놈이야
- Query : 반환 타입이 명확하지 않을 때 사용하는 놈이야
1 | TypedQuery<Member> query = |
이렇게 말이야!
타입이 정해지지 않은 경우에도 TypeQuery를 쓸 수 있어!! 어떻게?? Object.class를 이용해서 받으면 되지!!
결과 조회 API
- query.getResultList() : 결과가 하나 이상일 때, 리스트 반환해줘!! 만약 없다면 빈 리스트 반환해줘
- query.getSingleResult() : 결과가 정확히 하나일 때 사용해!! 정확히 하나!! 만약 결과가 없거나 둘 이상이면 각각 NoResultException, NonUniqueResultException 예외 던져
파라미터 바인딩 (이름 기준, 위치 기준)
파라미터 바인딩에는 크게 이름 기준과 위치 기준이 있는데 그냥 이름 기준을 사용해!! 아무래도 위치 기준은 이후 수정이 됬을 때 오류를 범하거나 귀찮아질 확률이 크닝께!
1 | SELECT m FROM Member m where m.username=:username |
프로젝션
프로젝션은 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 | SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM |
(Spring Data Jpa + QueryDSL 조합으로 가게되면 훨씬 편하게 가능해)
페이징 API
JPA는 페이징을 아래의 두 API로 추상화해서 제공해줘!!
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
- setMaxResults(int maxResult) : 조회할 데이터 수
1 | String jpql = "select m from Member m order by m.name desc"; |
이런식으루 말야!!
조인
- 내부 조인 : 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 절
- 조인 대상 필터링
- 연관곤계 없는 에티티 외부 조인
조인 대상 필터링
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 | select m from Member m |
👏👏👏 주의 사항 👏👏👏
FROM 절의 서브 쿼리는 JPQL에서 불가능해!!
조건식 - CASE 식
1 | //기본 CASE 식 |
IF와 SWITCH의 차이와 비슷한 느낌이라고 생각하면 될 것 같애!!
- COALESCE : 하나씩 조회해서 NULL이 아니면 반환 (예를들어 이름이 없는 사람들에 대해 ‘이름 없는 회원’ 으로 조회하고 싶을 때 사용하면 된다)
- NULLIF : 두 값이 같으면 NULL 반환, 다르면 첫번째 값 반환!! (특정 값에 해당하는 데이터를 가려야할 때)
1 | select coalesce(m.username,'이름 없는 회원') from Member m |
JPQL 기본함수
- CONCAT - 문자열 이어 붙일 때
- SUBSTRING - 특정 위치 문자열 추출할 때
- TRIM - 공백 제거
- LOWER, UPPER - 대소문자화
- LENGTH - 문자열길이
- LOCATE - 특정 문자열의 시자 위치
- ABS, SQRT, MOD
- SIZE - 특정 엔티티와 연관관계에 있는 엔티티들을 가리키는 LIST형 참조변수의 크기
- 사용자 정의 함수도 사용할 수 있어. 쓸일 있을 때 찾아서 공부해서 쓰길..