10. 객체 지향 쿼리 언어1 - 기본
#
소개#
JPA는 다양한 쿼리 방법을 지원JPQL
- JPA Criteria
QueryDSL
- Native SQL
- JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용
#
JPQL 소개- 가장 단순한 조회 방법
- EntityManager.find()
- 18상 이상인 사람 모두 불러오려면 어떻게?
#
JPQL- JPA를 사용하면 엔티티 객체 중심으로 개발 해야함
- 문제는 검색 쿼리
- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
- 앱이 필요한 데이터만 DB에서 불러오려면 결구 검색 조건이 포함된 SQL이 필요함!
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
- JPQL은 엔티티 객체를 대상으로 쿼리
- SQL은 테이블을 대상으로 쿼리
JPQL 을 한마디로 객체 지향 쿼리
#
Criteria 소개Criteria 대신 QueryDSL 사용 권장
- JPQL을 문자열 더하는 문제가 옴
- 동적 쿼리로 전환하면 좋음
- 그 대안으로 Criteria 나옴
- 음 그런데 오히려 가독성이 떨어지는 것 같음
#
장점- 동적쿼리 생성 시 컴파일 시점에 잡아줄 수 있음
- 동적쿼리를 짜기 편함
#
단점- SQL 스럽지 않음...
- 실무에서 거의 안쓰신다고 함
- 한두번 써봤는데 유지보수가 안됨...
#
QueryDSL 소개JPQL이 근본, 기승전 JPQL
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
- JPQL 빌더 역할
- 컴파일시 오류검출 가능
- 동적쿼리 작성이 편함
- 단순하고 쉬움
- 실무 사용 권장
#
Native SQL 소개- JPA가 제공하는 SQL을 직접 사용하는 기능
- JPQL로 해결할 수 없는 특정 DB에 의존적인 기능
#
JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용- 직접 커넥션 얻어서 사용
- JPA 쿼리들은 commit, query 시 flush 됨
- 영속성 컨텍스트를 플러시해야 후에 JdbcTemplate 을 문제없이 사용 가능
#
기본 문법과 쿼리 APIJPQL (Java Persistence Query Language)
#
JPQL 문법select m from Member as m where m.age > 18
- Entity와 속성은 대소문자 구분 O (Member, m.age)
- JPQL 키워드는 대소문자 구분 X (SELECT, From, where)
- 엔티티 이름(
@Entity(name = "")
) 사용, 테이블 이름이 아님 - 별칭은 필수(m) (as 는 생략 가능)
#
집합과 정렬- group by, order
- having
#
TypeQuery, Query- TypeQuery: 반환 타입이 명확할 때 사용
- Query: 반환 타입이 명확하지 않을 때 사용
#
결과조회query.getResultList()
: 결과가 하나 이상일 때- 결과가 없으면 빈 리스트 반환
query.getSingleResult()
: 결과가 정확히 하나- 결과가 없으면: javax.persistence.NoResultException
- 결과가 두개 이상이면: javax.persistence.NonUniqueResultException
- Spring Data JPA 에서는
- Exception 보단...
- null이나 Optional 반환
#
파라미터 바인딩#
이름 기준#
위치 기준- ?1 이런 식으로 가능하나 권장하지 않음
#
프로젝션 (SELECT)- SELECT 절에 조회할 대상을 지정하는 것
- 대상: 엔티티, 임베디드 타입, 스칼라 타입 (primitive type)
- Entity
SELECT m FROM Member m
SELECT m.team FROM Member m
- Embedded
SELECT o.adress from Order o
- Scalar
SELECT m.age FROM Member m
- DISTINCT 로 중복 제거
#
여러 값 조회1. Query 타입 조회
2. Object[] 타입 조회
3. new 명령어 조회
- 패키지명을 다 적어야 한다는 단점이 있음
- 순서와 타입이 일치하는 생성자 필요
#
페이징setFirstResult(int startPosition)
: 조회 시작 위치setMaxResults(int maxResult)
: 조회할 데이터 수
#
조인- 내부 조인
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
- Member row * Team row만큼 Row가 나옴...
#
ON 절#
1. 조인 대상 필터링회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
- JPQL
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
- SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'
#
2. 연관관계 없는 엔티티 외부 조인회원 이름과 팀의 이름이 같은 대상 외부 조인
- JPQL
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
- SQL
- 비슷함
#
서브 쿼리- 나이가 평균보다 많은 회원
select mfrom Member m where m.age > (select avg(m2.age) from Member m2)
- 한건이라도 주문한 고객
- 아래와 같이 하면 서브쿼리와 본 쿼리의 Member m을 공유해서 성능이 느려짐
select m from Member m where (select count(o) from Order where m = o.member) > 0
- 아래와 같이 하면 서브쿼리와 본 쿼리의 Member m을 공유해서 성능이 느려짐
#
서브 쿼리 예제팀 A 소속인 회원
전체 상품 각각의 재고보다 주문량이 많은 주문들
어떤 팀이든 팀에 소속된 회원
#
JPQL 서브 쿼리의 한계 ⭐️- JPQL 는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
- SELECT 절도 가능 (하이버네이트 지원)
FROM 절의 서브 쿼리는 현재 JPQL에서 불가능
- 조인으로 풀 수 있으면 풀어서 해결
- 앱에서 가져온걸 조립하거나, 쿼리를 여러개로 풀기도 함
- 정안되면 native query
#
JPQL 타입 표현과 기타식- 문자:
'Hello'
'What!'
- 숫자: long
20L
, double20D
, float20F
- Boolean:
TRUE
,FALSE
- ENUM:
jpabook.MemberType.Admin
(패키지명 포함)- setParameter로 대체할 수 있음
- 엔티티 타입:
TYPE(m)
= Member (상속 관계에서 사용)
그리고 표준 SQL 은 다 지원한다고 보면 됨
#
조건식 (CASE 등등)- COALESCE: null 이면 지정된 값 반환
- NULLIF
#
JPQL 함수- CONCAT
- SUBSTRING
- TRIM
- ...
- SIZE, INDEX (JPA 용도)
- 컬렉션 크기
select size(t.members) from Team t
- INDEX 는 안쓰는게...
- 컬렉션 크기
#
사용자 정의 함수 호출- 사용자 정의함수를 직접 등록해서 쓸 수 있음
- 보통 DB마다 기본적으로 제공하는 함수들은, Hibernate Dialect Function으로 등록되어 있었음
group_concat
function이 DB에 등록되어 있어야함
#
추가적으로- lombok 에 equals HashCode
- JPQL, queryDSL
- 의문사항 ElementCollection