[Java ORM 표준 JPA 프로그래밍] JPQL이 뭐야?-1

Junho Bae·2021년 3월 12일
5

SPRING JPA

목록 보기
3/5
post-custom-banner

인프런 김영한님의 Java ORM 표준 JPA 프로그래밍에서 다루는 JPQL 부분입니다. 오타주의, 구어체 주의

객체지향 쿼리언어 -1

JPA는 다양한 쿼리 방법을 지원함 : JPQL, JPA Criteria, QueryDSL, 네이티브 SQL - 특정 디비에 종속적,JDBC API 직접 사용, 마이바티스 등등

대부분의 경우 JPQL로 해결 가능, 아주 소수의 경우 나머지로 활용.

1.JPQL 소개 :

지금까지 가장 단순한 조회방법 : EntityManeger.find(), 객체 그래프 탐색.
나이가 18살 이상인 회원을 모두 가져오고 싶다면?

2.JPQL

JPA를 사용하면 엔티티 객체를 중심으로 개발. 디비는 존재만 할 뿐
문제는 검색 쿼리!

검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색

  • 모든 db를 객체로 변환해서 검색 쌉불가능

  • 어플리케이션이 필요한 데이터만 디비에서 불러오려면 결국 검색 조건이 포함된 sql이 필요

  • PA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공.

  • JPQL은 엔티티 객체 대상을 하는 쿼리. 결국 sql로 변환됨.

Ex)

em.createQuery(“select m from member(<-테이블이 아님, 엔티티) m where m.username like ~)

=> sql로 번역됨.
차이점이 몇가지가 있을 뿐. Select m 은 멤버 객체 자체를 조회해 와! 하는 의미
테이블이 아닌 객체를 대상으로 하는 쿼리.
“객체지향 sql”

3. Criteria 소개

쿼리는 사실 그냥 문자열일 뿐. 그러면 동적으로 쿼리를 생성하기가 어려움…

String username; 
If (username != null) {String where = “where m.username like ‘%Kim’’; qlstring + where …..}
//이러면 막 띄어쓰기 무슨 뭐 글자 바꾸기 이런거가 너무 빡셈. 딱 버그나기 너무 좋음. 
//Criteria 사용 준비 - java 표준 스펙에서 지원하는 문법
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);

Root<Member> m = query.from(Member.class);
query.select(m).where(cb.equal(m.get(“username”))
  • 실무에서 안씀ㅋㅋ 아 이런게 있구나. 이건 좀 망한 스펙입니다. 내가 쓴 코드를 내가 못알아봐요. 유지보수 쌉불가능. 차라리 스트링을 더하는게 나을 정도.

  • QueryDSL 사용. 요거는 김영한님 QueryDSL 강의에서.

  • 문자가 아닌 자바 코드로 JPQL을 작성

  • 동적 쿼리 작성이 편리. 단순하고 쉬움, 실무 사용 권장. 이걸 기본으로 깔고 들어갑니다. 동적 쿼리에서 강하기 때문.

중요한건 jpql 문법을 알면 됩니다. QueryDSL은 그러면 보너스처럼 먹고 들어간다고 보면 됩니다.

뜬금없긴 한데, Jpa와 관련 기술이 아닌 경우 플러시를 해줘야 하는거 잊지 말기.

*결론 jpql 문법 지리게 공부하고, querydsl이랑 같이 쓰세요.


4. 기본 문법과 쿼리 API

1) 쿼리 문법

  • 엔티티와 속성은 대소문자 구분
  • 키워드는 구분 x
  • 엔티티 이름 사용, 테이블 이름 아님.
  • 별칭은 필수 (m)
    select m from Member (as) m where sum.age>18

2) 집합과 정렬

Select
count(m)
sum(m)
~
Groub py, having 그대로 사용 가능

3) TypeQuery, Query

  • TypeQuery : 반환 타입이 명확할 떄 사용
TypeQuery<Member> query = em.createQuery(“select m from Member m”, Member.class) 
TypeQuery<String> query = em.createQuery(“select m.username from Member m”,String.class) 

Query : 반환 타입이 명확하지 않을 때 사용

4. 결과조회 api

TypeQuery<Member> query = em.createQuery(“select m from Member m”, Member.class) 

1) List resultlist = query.getResultList();
결과가 없으면 빈 컬렉션이 반환됨
널 포인터에 면역이 좀 있음.

2) Member result = query.getSingleResult();

  • 결과가 없으면 : noResultException (왜 또 트라이 캐치 써야대..좀 별로에요. 논란이 많아요.)
  • 결과가 두개 이상이면 : notUniqueException
    진짜로 값이 있고 보장이 될 때 써야됨.
    Spring Data Jpa -> 결과가 없으면 optional로 반환. 까보면 트라이 캐치 합니다 ㅋㅋ…

5. 파라미터 바인딩 - 이름기준, 위치 기준

TypeQuery<Member> query = em.createQuery(“select m from Member m where m.username = :username”, Member.class)
query.setParameter(“username”,”member1”);
.setParameter(“username,”member1”) 
.getSingleResult() //체이닝
// 위치기반으로 쓸 수도 있지만, 별롭니다. 이름 기준으로 쓰세용. 

6. 프로젝션 : select절에 조회할 대상을 지정하는 것.

1) 프로젝션

프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라타입 (숫자, 문자 등 기본 데이터 타입). rdbms는 스칼라만 가능했는데

select m from member m // 엔티티 프로젝션
Select m.team from member m  // 엔티티 프로젝션
Select m.address from member m // 임베디드 타입 프로젝션
Select m.usernmae , m.age from member m // 스칼라타입 프로젝션
//Distinct로 중복 제거

엔티티 프로젝션 : 영속성 컨텍스트로 다 관리가 됨.

List<Member> result = em.createQuery(“select m from member m”,Member.class).getResultList
  • 여기서 result의 친구들은 영속성 컨텍스트에 관리가 될까?
    -> 전부 다 영속성 컨텍스트에 관리가 됩니다.

엔티티 프로젝션

List<Team> result = em.createQuery(“select m.team from member m”, Team.class).getResultList();
  • 조인 쿼리가 나갑니다. 하지만 sql이랑 최대한 비슷하게 사용해야 합니다. 따라서 경로표현식인
List<Team> result = em.createQuery(“select m.team from member m join m.team team”, Team.class).getResultList();

과 같이 명시적으로 조인을 표현해줘야 합니다.

임베디드 타입 프로젝션

em.createQuery(“select o.address from Order o”,Address.class) - 임베디드 타입 프로젝션
  • 임베디드 타입으로만은 안되고, 그거가 속해있는 엔티티로부터 시작해야함.

2) 프로젝션 - 여러 값 조회

List resultList = em.createQuery(“select m.username, m.age from member m”) .getResultList();
1. 쿼리타입으로 조회
Object o = resultList.get(0);
Object[] result = (Object[]) 0;
2. 오브젝트 배열 타입으로 조회
List<Object[]> resultList = em.createQuery(“select m.usernmae ~~~~) 이런식으로

3. new 명령어로 조회.

memberDTO를 만듬.

//Member DTO class
public class MemberDTO {
	private String username;
	private String age;

	//getter()
	//setter()
}

//test file
List<MemberDTO> result = em.createQuery(“select new jpql.MemberDTO(m.username, m.age) from member m”, MemberDTO.class).getResultList();

그나마 깔끔한데 패키지 이름을 new에다가 다 해줘야됨.
querydsl쓰면 됨.
순서와 타입이 일치하는 생성자가 필요함.


7. 페이징

아트의 경지입니다… 다른 db쓰면 거지같은데 jpa에서는 환상적으로 해줍니다.

setFirstResult(int startPosition) : 조회 시작 위치
setMax

em.createQuery(“select m from member m order by m.age desc”) - 페이징에서는 오더바이가 들어가야’
	.setFirstResult(1)
	.setMaxResult(10)
	.getResult();

하면…? 에뮬레이팅 해줌. 개꿀쓰~
이 쿼리가 그 persistence.xml에 있는 dialect로 조져줌.
————————————————————————————————————

8. 조인 : 엔티티 중심으로 조인 문법이 나감.

내부조인 : select m from member m [inner] join m.team t 회원과 연관있는 team을 조인할거야
외부조인 : select m from member m left join m.team t
세타조인 : “막조인”. 이상한 쿼리 날릴 수도 있잖아. 연관관계가 전혀 없는 애를 하고 싶을 때.

String query = “select m from member m inner join m.team t where t.teamnae = :teamName” 이런식으로
String query = “select m from member m left join m.team t where t.teamnae = :teamName” 이런
String query = “select m ffrom member m, team t  where m.username = t.teamnae”
  • on 절 : jpa 2.1부터 지원
    조인 대상 필터링
    ex) 회원과 팀을 조인하면서 팀 이름이 a인 팀만 조인
    jpql:
    select m,t from member m left join m.team t on t.name = ‘a’

연관관계 없는 엔티티 외부 조인 (하이버네이트 5.1), 기존에는 내부 조인만 그랬는데 이제는 외부 조인까지 가능
ex) 회원의 이름과 팀의 이름이 같은 대상 외부 조인 - 스프링 높은 버전을 써야. 5.1 이상의 하이버네이트
jpql :
select m,t from member m left join team t on m.username = t.name
-> 서로 아무 연관관계가 없어도 left join 가능


9. 서브 쿼리

나이가 평균보다 많은 회원

select m from member m where m.age > (select avg(m2.age) from member m2)
한 건이라도 주문한 고객
select m from member m where (select count(0) from Order o where m = o.member)>0

jpa 서브쿼리의 한계
jpa는 where,having 절에서만 서브 쿼리 사용 가능
select절도 가능. 하이버네이트 지원
from 절의 서브 쿼리는 현재 jpql에서 불가능 - 조인으로 풀 수 있으면 풀어서 해결, 안되면 포기. 안됨.


5. jpql 타입 표현

6. 조건 케이스식

  • 기본 케이스식
  • 단순 케이스식
    coalesce : 하나씩 조회해서 Null이 아니면 반환
    nullif : 두 값이 같으면 null 반환, 다르면 첫 번째 값 반환

7. jpql 함수 - 기본함수/사용자정의함수/

1) 기본함수 : 디비에 상관없이 jpql이 지원하는 함수.
2) 사용자 정의 함수 : 디비에 함수가 있으면 그걸 가져다가 써야 하는데, jpql은 그 함수를 알 방법이 없음
select function(‘group_concat’,i.name) from item i
근데 이걸 방언으로 등록해놔야함..
하지만, 다행히 이미 그런 디비에서 쓰는 함수를 등록을 해놨다~ 이거야~ 물론 디비 종속적이지만..

//ex)concat
String query = “select ‘a’ || ‘b’ from member m”  = concat(‘a’,’b’)
//ex) substring
String query = “select substring(m.username,2,3) from member m” …
//ex) locate
String query = “select locate(‘de’,’abcdefg’) from member m”
//‘de’의 자리 4가 나옴. 

ex) length, abs, sqrt, mod

ex) size, index (jpa 용도)

String query = “select size(t.members) from team t” - 컬렉션의 크기를 돌려줌
String query = “select index(t.members) from team t”

  • @OrderColmn 뭐 이런거..?


객체지향 쿼리언어2 - 중급문법

1.경로 표현식

경로표현식? : 점을 찍어 객체 그래프를 탐색하는 것

select m.username //상태 필드
	from member m
	join m.team t // 단일 값 연관 필드(manytooe,onetoone..)
	join m.orders o // 컬렉션 값 연관 필드
where t.name = "팀A"
//상태필드로 가냐, 단일 값 연관 필드로 가냐, 컬렉션 값 연관 필드로 가냐에 따라서 결과가 달라짐.                       

상태필드 : 단순히 값을 저장하기 위한 필드 ex) m.username

  • 특징 : 경로 탐색의 끝. 더이상 탐색 x
  • m.username 하고 또 .을 찍어서 어딜 갈 수가 없음. 끝임

연관필드 : 연관 관계를 위한 필드

1) 단일 값 연관 경로 : @ManyToOne, @OneToOne, 대상이 엔티티
- 묵시적 내부조인 발생, 탐색 가능.
- select m.team.username 등등, 탐색 가능.

	String query = "select m.team from member m"
	List<Team> result = em.createQuery(query,Team.class).getResultList()
  • 묵시적 조인이 발생함. 팀을 찾는. 계속 조인문이 나갈 거임.
    - 이렇게 하면 안되겠구나 생각을 해야함

2) 컬렉션 값 연관 필드 : @OneToMany, @ManyToMany

  • 묵시적 내부 조인 발생, 탐색은 안됨.
    String query = "select t.members from member t"
    Collection result = em.createQuery(query, Collection.class).getResultList()
  • 핵심이 뭐냐, .찍었는데 나오냐 안나오냐라는거야. 이건 컬렉션 그 자체이기 때문에 뭐 할 수 있는게 없어. size 이정도 밖에?
  • 그러면 컬렉션으로 가져온것의 이름을 가져오고 싶다? from절에서 조져서 별칭을 가져와야됨. (명시적 조인을 하자)
    String query = "select m.username from team t join t.members;"

결론 : 사실 사 무시하고, 그냥 묵시적 조인을 쓰지 마세요!! 명시적 조인을 써야 합니다. 그래야 쿼리 튜닝이 가능합니다. 묵시적 조인 스면 실무에서 상당한 에러가 발생한다. -> 아 뭐 신경 안쓰면 되겠네.

명시적 조인? : 조인 키워드 직접 사용
묵시적 조인? 경로 표현식에 의해 묵시적으로 sql조인 발생. 외부조인 불가능

예제

select o.member.team from order o //성공,문제는 조인이 두번 일어나겠죠. 

select t.members from team // 성공, 컬렉션이지만 더 안들어갔으니까. 끝이니까

select t.members.username from team t //실패, 컬렉션은 더 못들어감

select m.username from team t join t.members m // 조인문을 조져줬기 때문에.

진짜 결론 : 경로 탐색을 사용한 묵시적 조인의 주의사항
1) 묵시적 조인은 항상 내부 조인
2) 컬렉션은 항상 경로 탐색의 끝, 명시적 조인을 통해 별칭을 얻어야.
3) 경로 탐색은 select where에서만 사용하지만 sql에서는 from, where에 영향

-가급적 묵시적 조인 대신에 명시적 조인 사용합시다.
-조인은 sqlㅇ 튜닝에 대한 중요 포인트.
-묵시적 조인은 조인이 일어나는 상황 자체에 대한 판단이 어려움.

2. 패치 조인 : 정말 정말 정말 중요함. 못하면 실무를 못할 정도.

하지만 나중에.

profile
SKKU Humanities & Computer Science
post-custom-banner

0개의 댓글