참고
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이 없다
•
JPQL에 JOIN이 없어도 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
•
JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용이 가능하다.
•
SELECT 절도 가능 (하이버네이트에서 지원)
•
FROM 절의 서브 쿼리는 불가능하다! → JOIN으로 풀 수 있으면 풀어서 해결함