Search
Duplicate
📒

[JPA 기본] 10-1. JPQL - 기본 문법(1)

상태
완료
수업
JPA
주제
JPA
4 more properties
참고

JPQL(Java Persistence Query Language)

NOTE
SQL을 추상화한 객체 지향 쿼리 언어!
JPA를 사용하면 엔티티 객체를 중심으로 개발하는데, 검색쿼리에서 문제가 생긴다.
검색을 할 때도, 테이블이 아닌 엔티티 객체를 대상으로 검색해야함.
그래서 SQL을 대신해서 JPQL을 사용해서 엔티티 객체를 대상으로 쿼리를 작성한다.
동적쿼리에 약하다 → 이후 QueryDsl에서 해결가능
JPQL은 결국 SQL로 변환된다.

JPQL 기본문법

JPQL은 별칭을 필수로 사용해야 한다. (as는 생략가능)
엔티티의 이름을 사용해야한다! (테이블명 아님)
JPQL 키워드는 대소문자를 구분하지 않는다. (SELECT, FROM, WHERE)
엔티티속성은 대소문자를 구분한다. (Member)

결과 조회

NOTE

query.getResultList()

결과가 하나 이상일 때 리스트를 반환한다.
결과가 없다면 빈 리스트를 반환

query.getSingleResult()

결과가 정확히 하나일 때 사용한다.
결과가 하나만 나오는 경우가 아니면 예외를 뱉어내므로 사용에 주의하자.
결과가 없다 → javax.persistence.NoResultException
둘 이상이다 → javax.persistence.NonUniqueResultException

TypeQuery, Query

NOTE

TypedQuery

TypedQuery<Member> query = em.createQuery("SELECT m FROM Meber m", Member.class);
Java
복사
Meber 타입이 결과로 나온다.
반환 타입이 명확할 때 사용된다.

Query

Query query = em.createQuery("SELECT m.username, m.age FROM Meber m");
Java
복사
결과가 String, Int 2개가 나온다
반환 타입이 명확하지 않을 때 사용한다.

프로젝션(SELECT)

NOTE
SELECT 절에 조회할 대상을 지정하는 것
@Entity @Getter @Setter public class Member { @Id @GeneratedValue private Long id; private String userName; private int age; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "TEAM_ID") private Team team; @Enumerated (EnumType. STRING) private MemberType type; public void changedTeam(Team team){ this.team = team; team.getMembers().add(this); } }
Java
복사

검색 대상

SELECT m FROM Member m엔티티 프로젝션
SELECT m.team FROM Member m엔티티 프로젝션
SELECT m.address FROM Member m임베디드 타입 프로젝션
SELECT m.username, m.age FROM Member m스칼라 타입 프로젝션
DISTINCT로 중복 제거

묵시적 조인, 명시적 조인

NOTE

묵시적 조인

List<Team> result = em.createQuery("select m.team from Member m", Team.calss) //SQL: SELECT t.id, t.name FROM Member m inner join TEAM t on m.team_id = t.id
Java
복사
JPQL에서는 JOIN이 없다
JPQLJOIN이 없어도 Team Entity를 조회하기 위해서 JOIN을 해준다.

명시적 조인

List<Team> result = em.createQuery("select t from Member m join m.team t", Team.calss) //SQL: SELECT t.id, t.name FROM Member m inner join TEAM t on m.team_id = t.id
Java
복사
JPQL에서는 JOIN을 명시함
묵시적 조인과 차이는 없지만, 명시적으로 JPQL에 적어주는것이 가독성에 주고, 예측하기 좋다.

임베디드 타입 프로젝션

em.createQuery("select distinct m.address, m.age from Member m").getRresultList();
Java
복사
address는 ENUM타입이다.
임베디드 타입의 경우 따로 JOIN을 하지는 않는다. (테이블은 하나이기 때문에)
FROM절에 적을수는 없음

스칼라 타입 프로젝션 / 여러 값 조회

NOTE
em.createQuery("select distinct m.address, m.age from Member m").getRresultList();
Java
복사
반환 타입이 String, int 여러개
1.
Query 타입으로 조회
2.
Object[] 타입으로 조회
3.
new 명령어로 조회
단순 DTO로 바로 조회도 가능함
SELECT new jpabook.jpql.UserDTO(m.username, m.age)from Memer m;
패키지명을 포함한 전체 클래스명을 적어줘야함.
순서와 타입이 일치하는 생성자 필요.

페이징

NOTE
JPA의 페이징은 매~우 간단하다! 단 2가지만 알면된다!
em.createQuery("select m from Member m order by m.age desc", Member.class) .setFirstResult(0) .setMaxResults(10) .getReulstList();
Java
복사
처음부터 10개 들고오기!
setFirstResult(int startPosition): 조회 시작 위치(0부터 시작)
setMaxResults(int maxResult): 조회할 데이터 수

각 데이터베이스 별로 맞춰서 페이징 해줌!

SELECT M.ID AS ID, M.AGE AS AGE, M.TEAM_ID AS TEAM_ID, M.NAME AS NAME FROM MEMBER M ORDER BY M.NAME DESC LIMIT ?,?
SQL
복사
MySQL 방식
SELECT * FROM (SELECT ROW_.*, ROWNUM ROWNUM_ FROM (SELECT M.ID AS ID, M.AGE AS AGE, M.TEAM_ID AS TEAM_ID, M.NAME AS NAME FROM MEMBER M ORDER BY M.NAME ) ROW_ WHERE ROWNUM <= ? ) WHERE ROWNUM_ > ?
SQL
복사
Oracle 방식

조인

NOTE

내부조인

SELECT m FROM Member m INNER JOIN m.team t
Java
복사

외부조인

SELECT m FROM Member m LEFT OUTER JOIN m.team t
Java
복사

세타 조인

select count(m) from Member m, Team t where m.username = t.name
Java
복사

조인 - ON 절

NOTE

1. 조인 대상 필터링

ex: 회원과 팀을 조인하면서, 팀 이름이 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. 연관관계 없는 엔티티 외부 조인

ex: 회원의 이름과 팀의 이름이 같은 대상 외부조인
JPQL: SELECT m,t FROM Member m LEFT JOIN Team t on m.username = t.name
SQL: SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.username

서브 쿼리

NOTE

EX_1. 나이가 평균보다 많은 회원

select m from Member m where m.age > (select avg(m2.age) from Member m2);
Java
복사
where절 서브쿼리

EX_2. 한 건이라도 주문한 고객

select m from Member m where (select count(o) from Order o where m=o.member) > 0
Java
복사
where절 서브쿼리

서브 쿼리 지원 함수

NOTE

[NOT] EXISTS(subquery)

서브쿼리에 결과가 존재하면 참

{ALL | ANY | SOME} (subquery)

ALL 모두 만족하면 참
ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참

[NOT] IN(subquery)

서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

예제

select m from Member m where exists (select t from m.team t where t.name = '팀A')
Java
복사
팀A 소속인 회원
select o from Order o where o.orderAmount > ALL(select p.stockAmount from Product p)
Java
복사
전체 상품 각각의 재고보다 주문량이 많은 주문들
select m from Member m where m.team = ANY(select t from Team t)
Java
복사
어떤 팀이든 팀에 소속된 회원

JPA 서브 쿼리 한계

NOTE
JPAWHERE, HAVING 절에서만 서브 쿼리 사용이 가능하다.
SELECT 절도 가능 (하이버네이트에서 지원)
FROM 절의 서브 쿼리는 불가능하다! → JOIN으로 풀 수 있으면 풀어서 해결함