JPQL (Java Persistence Query Language) 란?
JPA (Java Persistence API) 의 일부로 정의된 플랫폼 독립적인 객체지향 쿼리 언어이다.
객체지향 쿼리이므로 테이블이 아닌 엔티티를 대상으로 한다.
JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다. 하지만 결국 SQL로 변환된다.
SQL과 비슷하게 SELECT, UPDATE, DELETE 문을 사용할 수 있다.
(참고로 엔티티 저장은 그냥 entityManager.persist를 사용하면 되므로 INSERT 문은 없다.)
JPQL에서 UPDATE, DELETE 문은 벌크 연산이라고 해서 뒤에서 따로 설명할 것이고, SELECT 문 먼저 작성하겠다.
기본 문법
SELECT m FROM Member AS m WHERE m.username = 'Hello'
1. 대소문자 구분
- 엔티티와 속성은 대소문자를 구분한다. Member와 member는 다르고 username과 USERNAME은 다르다.
- SELECT, FROM 같은 JPQL 키워드는 대소문자를 구분하지 않는다.
2. 엔티티 이름
- FROM 이후에 오는 대상은 테이블 이름이 아니라 엔티티 이름이다.
3. 별칭(Alias) 사용 필수
- JPQL은 별칭을 필수로 사용해야 한다. AS 뒤에 m이 Member의 별칭이다.
- AS는 생략 가능하다.
TypedQuery, Query
작성한 JPQL을 실행시키기 위해 만드는 쿼리 객체이다.
JPQL이 반환할 타입을 명확하게 지정할 수 있으면 TypedQuery를 사용하고, 명확하게 지정할 수 없으면 Query를 사용하면 된다.
// 조회대상이 정확히 Member 엔티티이므로 TypedQuery 사용 가능
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
// 조회대상이 String, Integer로 명확하지 않으므로 Query 사용
Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
TypedQuery로 실행된 쿼리는 두번째 인자로 주어진 클래스를 반환하고,
Query의 경우 예제처럼 조회 컬럼이 1개 이상일 경우 Object[], 1개일 경우 Object를 반환한다.
결과 조회
쿼리 객체에서 아래의 메서드들을 사용해 JPQL을 실행한다.
- query.getResultList()
- 결과를 컬렉션으로 반환한다. 결과가 없으면 빈 컬렉션이 반환된다. 1건이면 1건만 들어간 컬렉션이 반환된다.
- query.getSingleResult()
- 결과가 정확히 1건 일때 사용한다. 결과가 없으면 javax.persistence.NoResultException, 결과가 1건 이상이면 javax.persistence.NonUniqueResultExceptio 이 발생한다. (근데 얘는 Optional을 반환해야 하지 않을까?)
파라미터 바인딩
이름 기준 파라미터 바인딩을 지원한다.
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m WHERE m.username = :username", Member.class)
.setParameter("username", "joont1"); // JPQL은 대부분 메서드 체인 방식으로 되어있어서 이렇게 연속해서 작성하는 것이 가능하다
List<Member> result = query.getResultLst();
username은 Member 클래스에 정의된 프로퍼티 이름이다. 앞에 :를 붙여서 바인딩한다.
username 에 joont1 이 바인딩 될 것이다.
참고로 아래와 같이 위치 기준 파라미터 바인딩도 지원하기는 한다.
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m WHERE m.username = ?1", Member.class)
.setParameter(1, "joont1");
이것보다는 전자가 더 명확하다.
참고로 LIKE 연산처럼 % 같은 특수문자가 필요할 경우 전달하는 파라미터에 붙여서 사용하면 된다.
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m WHERE m.username LIKE :username", Member.class)
.setParameter("username", "%joont%"); // 이런식으로
파라미터 바인딩 방식은 선택이 아닌 필수이다
- JPQL에 직접 문자를 더하면 SQL Injection을 당할 수 있다
- JPA에서 파라미터만 다를 뿐 같은 쿼리로 인식하므로, JPQL을 SQL로 파싱한 결과를 재사용할 수 있다
- SQL 내에서도 같은 쿼리는 결과를 재사용한다
프로젝션
조회할 대상을 지정하는 것을 프로젝션이라고 한다.
SELECT [프로젝션 대상] FROM 으로 대상을 지정한다.
대상은 엔티티 타입, 임베디드 타입, 스칼라 타입이 있다.
엔티티 타입
SELECT m FROM Member m // member
SELECT m.team FROM Memher m // team
둘 다 엔티티를 프로젝션 대상으로 사용했다.
참고로 이렇게 조회한 엔티티는 영속성 컨텍스트에서 관리된다.
임베디드 타입 프로젝션
엔티티를 통해서 조회한다.
참조한 곳
https://joont92.github.io/jpa/JPQL/
'Spring' 카테고리의 다른 글
| Spring Data Jpa 의 Pagination (0) | 2023.03.01 |
|---|---|
| @Repository 어노테이션 (1) | 2023.02.27 |
| JPA 연관관계 (0) | 2023.02.16 |
| @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor (0) | 2023.02.13 |
| Spring WebFlux - Annotated Controllers, URI Links (0) | 2023.02.12 |