[JPA] 10. 객체지향 쿼리 언어 - JPQL
자바 ORM 표준 JPA 프로그래밍 공부 기록
목차
- 객체지향 쿼리 소개
- JPQL
- Criteria
- QueryDSL
- Native SQL
- 객체지향 쿼리 심화
📌 10.2 JPQL
-
객체지향 쿼리 언어
테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리함 -
SQL을 추상화해서 특정데이터베이스 SQL에 의존하지 않는다
📍 JPQL 문법
- 엔티티와 속성은 대소문자 구분 O (Member, age)
- JPQL 키워드는 대소문가 구분 X (SELECT ...)
- Entity 명 사용 (테이블 이름 X)
- 별칭은 필수 (Member m)
TypeQuery, Query
작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다.
쿼리 객체는 TypeQuery와 Query가 있다
- TypeQuery : 반환할 타입을 명확하게 지정할 수 있으면 사용
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
- Query : 반환 타입이 명확하지 않을 때 사용
Query query = em.createQuery("select m.userName, m.age from Member m");
결과 조회 API
-
query.getResultList
결과가 하나 이상일 때, 리스트 반환.
결과가 없으면빈 컬렉션 반환
-
query.getResultList
결과가 정확히 하나, 단일 객체 반환
예외 발생- 결과가 없으면:
javax.persistence.NoResultException
- 둘 이상이면:
javax.persistence.NonUniqueResultException
- 결과가 없으면:
📍 파라미터 바인딩
JDBC는 위치 기준 파라미터 바인딩만 지원하지만 JPQL은 이름 기준 파라미터 바인딩도 지원한다.
- 이름 기준 파라미터
이름 기준 파라미터는 앞에:
를 사용한다
String usernaeParam = "User1";
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username=:username", Member.class);
query.setParameter("username", usernameParam);
- 위치 기준 파라미터
?
다음에 위치 값을 주면 된다
String usernaeParam = "User1";
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username=?1", Member.class);
query.setParameter(1, usernameParam);
위치 기준 파라미터 방식 보다는 이름 기준 파라미터 바인딩 방식
을 사용하는게 더 명확하다.
SQL 인젝션 공격이나 전체적인 성능 향상을 위해 파라미터 바인딩 방식은 선택이 아닌 필수이다!
📍 프로젝션
SELECT 절에 조회할 대상을 지정하는 것을 프로젝션
이라고 한다.
프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타이
-
엔티티 프로젝션
em.createQuery("select m from Member m", Member.class).getResultList();
엔티티 프로젝션에서 조회한 엔티티는 영속성 컨텍스트에서 다 관리된다. -
임베디드 타입 프로젝션
em.createQuery("select o.address from Order o", Address.class).getResultList();
임베디드 타입은 엔티티 타입이 아닌 값 타입이다. 따라서 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다. -
스칼라 타입 프로젝션
문자, 숫자, 날짜와 같은 기본 데이터 타입을 스칼라 타입이라 한다.
new 명령어로 조회
public class MemberDTO {
private String userName;
private int age;
public MemberDTO(String userName, int age) {
this.userName = userName;
this.age = age;
}
}
List<MemberDTO> list = em.createQuery("select new jpql.MemberDTO(m.userName, m.age) from Member m", MemberDTO.class).getResultList();
select
다음에 new
명령어를 사용하면 반환받을 클래스를 지정할 수 있는데 이 클래스의 생성자에 JPQL 조회 결과를 넘겨줄 수 있다. 그리고 new
명령어를 사용한 클래스로 TypeQuery
를 사용할 수 있어 객체 변환 작업을 줄일 수 있다.
📍 페이징 API
• setFirstResult(int startPosition)
: 조회 시작 위치 (0부터 시작)
• setMaxResults(int maxResult)
: 조회할 데이터 수
데이터베이스 방언 (Dialect
) 덕분에 각기 다른 DB마다 같은 API로 페이징 처리를 할 수 있다.
📍 서브 쿼리
FROM
절의 서브 쿼리는 현재 JPQL에서 불가능
=> 조인으로 풀 수 있으면 풀어서 해결
📍 조건식
💭 타입 표현
-
문자
작은 따옴표 사이에 표현한다. 작은 따옴표 표현하려면 연속 두개(''
) 사용
ex)'Hello'
,'She''s'
-
숫자 :
10L(Long)
,10D(Double)
,10F(Float)
-
Boolean:
TRUE
,FALSE
-
Enum
패키지명을 포함한 전체 이름을 사용해야 한다
ex ) jpql.MemberType.ADMIN
String query = "select m.userName, 'HELLO', TRUE from Member m "
+ "where m.memberType = :userType";
List<Object[]> result = em.createQuery(query)
.setParameter("userType", jpql.MemberType.ADMIN)
.getResultList();
for(Object[] objects : result){
System.out.println(objects[0]);
System.out.println(objects[1]);
System.out.println(objects[2]);
}
Hibernate:
/* select
m.userName,
'HELLO',
TRUE
from
Member m
where
m.memberType = :userType */
select
member0_.userName as col_0_0_,
'HELLO' as col_1_0_,
1 as col_2_0_
from
Member member0_
where
member0_.memberType=?
TEST
HELLO
true
- 엔티티
엔티티의 타입을 표현한다. 주로 상속 관련해서 사용
ex)TYPE(m) = Member
💭 조건식 - CASE 식
특정 조건에 따라 분기할 때 CASE 식을 사용함. 종류는 다음과 같다
- 기본 CASE
String query = "select " +
"case when m.age <= 10 then '학생요금' " +
" when m.age >= 60 then '경로요금' " +
" else '일반요금' " +
"end "+
"from Member m " +
"";
List<String> result = em.createQuery(query, String.class)
.getResultList();
-
심플 CASE
조건식이 없고 문법이 단순 (자바의switch
와 유사) -
COALESCE
스칼라식을 차례대로 조회해서null
이 아니면 반환한다. -
NULLIF
Author And Source
이 문제에 관하여([JPA] 10. 객체지향 쿼리 언어 - JPQL), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jodawooooon/JPA-10.-객체지향-쿼리-언어-JPQL저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)