JPQL
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리언어 지원
- 테이블이 아닌 객체를 대상으로 검색
- 객체 지향 SQL
Creteria
유지보수 어려움
대신, QueryDSL 사용
JPQL 문법
1. TypeQuery, Query
- TypeQuery: 반환 타입이 명확할 때 사용
- Query: 반환 타입이 명확하지 않을 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
Query query = em.createQuery("SELECT m.username, m.age from Member m");
1-1. query.getResultList()
결과가 하나 이상일 때, 리스트 반환
1-2. query.getSingleResult()
결과가 정확히 하나, 단일 객체 반환
- 결과가 없거나 2개 이상일 경우 에러 발생
1-3. 파라미터 바인딩
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username=:username");
query.setParameter("username", usernameParam);
2. 프로젝션 : SELECT절 지정
SELECT m FROM Member m
SELECT distinct m.username, m.age FROM Member m
- 단순 값을 DTO로 바로 조회
SELECT new jpabook.jpql.UserDTO(m.username, m.age)
FROM Member m
3. 페이징 API
- setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
- setMaxResults(int maxResult) : 조회할 데이터 수
String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(0) // 0번 부터
.setMaxResults(10) // 10개
.getResultList();
4. 조인
- 내부 조인 : 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
Join의 On절 사용가능
SELECT m, t FROM Member m LEFT JOIN m.team t ON t.name = 'A'
5. 서브쿼리
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
서브쿼리 지원함수
- EXISTS
- ALL
- ANY, SOME(같은의미)
- [NOT] IN
select m from Member m where exists (select t from m.team t where t.name = ‘TEAM_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)
JPA 서브쿼리의 한계
FROM 절의 서브쿼리는 JPQL에서 불가능하다.
6. 조건식 - CASE식
- 기본 CASE식
- 단순 CASE식
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
- COALESCE : 하나씩 조회해서 NULL이 아니면 반환
select coalesce(m.username,'이름 없는 회원') from Member m
- NULLIF : 두 값이 같으면 NULL반환, 다르면 첫번째 값 반환
사용자 이름이 ‘관리자’면 null을 반환하고 나머지는 본인의 이름을 반환
select NULLIF(m.username, '관리자') from Member m
7. 엔티티 직접 사용
[JPQL]
select count(m.id) from Member m //엔티티의 아이디를 사용
select count(m) from Member m //엔티티를 직접 사용
- 결국 같다.
8. Named 쿼리
- 미리 정의해서 이름을 부여하고 사용하는 JPQL
- 정적쿼리
- 어노테이션, XML에 정의
- 애플리케이션 로딩 시점에 초기화 후 재사용 → 로딩 시점에 쿼리를 검증
@Entity
@NamedQuery(
name = "Member.findByUsername", // name
query="select m from Member m where m.username = :username") // 쿼리
public class Member {
}
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "회원1")
.getResultList();
9. 벌크 연산 executeUpdate()
- JPA 변경 감지 기능으로 실행하려면 너무 많은 SQL 실행
- 재고가 10개 미만인 상품을 리스트로 조회
- 상품 엔티티 가격을 10% 증가
- 트랜잭션 커밋 시점에 변경감지가 작동
- 변경된 데이터가 100건이면 100번의 update sql 실행
String qlString = "update Product p set p.price = p.price * 1.1 " +
"where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(qlString) // flush 자동 호출됨
.setParameter("stockAmount", 10)
.executeUpdate();
em.clear(); // 영속성 컨텍스트 초기화해주기
- 영속성 컨텍스트 무시 → DB에 직접 쿼리
- 연산 수행 후 영속성 컨텍스트 초기화해주기 (안해주면 꼬일 수 있다)
'공부 > Spring' 카테고리의 다른 글
순수 JPA 기반 리포지토리와 차이점을 확인해보자 (0) | 2022.08.25 |
---|---|
[JPA] 페치 조인 (0) | 2022.07.18 |
[JPA] 값 타입 (0) | 2022.07.02 |
[JPA] 프록시와 연관관계 관리(지연로딩) (0) | 2022.07.02 |
[JPA] 다양한 연관관계 매핑 (0) | 2022.07.02 |
댓글