JPA는 다양한 쿼리 방법을 지원하고 우리는 복잡한 쿼리를 짤 줄 알아야 한다!
이제 하나씩 알아보도록 하자🤗
// 검색
String jpql = "select m From Member m where m.name like ‘%hello%'";
List<Member> result = em.createQuery(jpql, Member.class)
.getResultList();
⬆️ 실행된 SQL
가장 단순한 조회 방법!!
➡️ EntityManager.find()
➡️ 객체 그래프 탐색(a.getB().getC()
)
검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색!!
그러나 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
➡️ 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검 색 조건이 포함된 SQL이 필요!
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
SQL과 문법 유사함
➡️ SELECT
, FROM
, WHERE
, GROUP BY
, HAVING
, JOIN
지원
JPQL은 엔티티 객체를 대상으로 쿼리 (SQL은 데이터베이스 테이블을 대상으로 쿼리)
- 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리
- SQL을 추상화해서 특정 데이터베이스 SQL에 의존X
- JPQL을 한마디로 정의하면 객체 지향 SQL
// Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
// 루트 클래스 (조회를 시작할 클래스)
Root<Member> m = query.from(Member.class);
// 쿼리 생성
CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), “kim”));
List<Member> resultList = em.createQuery(cq).getResultList();
📌 Criteria 대신에 QueryDSL 사용 권장!
// JPQL
// select m from Member m where m.age > 18
JPAFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list = query.selectFrom(m)
.where(m.age.gt(18))
.orderBy(m.name.desc())
.fetch();
String sql =
“SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = ‘kim’";
List<Member> resultList =
em.createNativeQuery(sql, Member.class).getResultList();
select m from Member as m where m.age > 18
Member
, age
)SELECT
, FROM
, where
)📌 집합과 정렬
select COUNT(m), //회원수 SUM(m.age), //나이 합 AVG(m.age), //평균 나이 MAX(m.age), //최대 나이 MIN(m.age) //최소 나이 from Member m
1️⃣ TypeQuery : 반환 타입이 명확할 때 사용
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m", Member.class);
2️⃣ Query : 반환 타입이 명확하지 않을 때 사용
Query query =
em.createQuery("SELECT m.username, m.age from Member m");
1️⃣ query.getResultList()
2️⃣ query.getSingleResult()
javax.persistence.NoResultException
javax.persistence.NonUniqueResultException
1️⃣ 이름 기준
SELECT m FROM Member m where m.username=:username
query.setParameter("username", usernameParam);
2️⃣ 위치 기준
SELECT m FROM Member m where m.username=?1
query.setParameter(1, usernameParam);
💡 프로젝션 : SELECT 절에 조회할 대상을 지정하는 것
프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타
입)
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로 중복 제거
SELECT m.username, m.age FROM Member m
1️⃣ Query
타입으로 조회
2️⃣ Object[]
타입으로 조회
3️⃣ new
명령어로 조회
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m
다른 것들과 다르게 JPA 페이징은 아트의 경지이다^_^
JPA는 페이징을 아래 두 API로 추상화했다❗️
1️⃣ **setFirstResult(int startPosition)**
➡️ 조회 시작 위치(0부터 시작)
2️⃣ **setMaxResults(int maxResult)**
➡️ 조회할 데이터 수
// 페이징 쿼리
String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(10)
.setMaxResults(20)
.getResultList();
➡️ setFirstResult
, setMaxResults
만 넣어주면 된다!
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 ?, ?
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_ > ?
1️⃣ 내부 조인
SELECT m FROM Member m [INNER] JOIN m.team t
2️⃣ 외부 조인
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
3️⃣ 세타 조인
select count(m) from Member m, Team t where m.username = t.name
1️⃣ 조인 대상 필터링 - 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'
2️⃣ 연관관계 없는 엔티티 외부 조인 - 회원의 이름과 팀의 이름이 같은 대상 외부 조인
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
일반적인 sql에서 말하는 서브 쿼리와 같은 의미!!
➡️ 쿼리 안에서 또 다른 서브 쿼리를 만들 수 있음
[NOT] EXISTS (subquery)
{ALL | ANY | SOME} (subquery)
ALL
: 모두 만족하면 참ANY, SOME
: 같은 의미, 조건을 하나라도 만족하면 참[NOT] IN (subquery)
1️⃣ 팀A 소속인 회원
select m from Member m where exists (select t from m.team t where t.name = ‘팀A')
2️⃣ 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)
3️⃣ 어떤 팀이든 팀에 소속된 회원
select m from Member m where m.team = ANY (select t from Team t)
WHERE
, HAVING
절에서만 서브 쿼리 사용 가능SELECT
절도 가능(하이버네이트에서 지원)FROM
절의 서브 쿼리는 현재 JPQL에서 불가능🤔 문자로 어떻게 표현할 것인가! 숫자로 어떻게 표현할 것인가!
‘’
사용1️⃣ 기본 CASE식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
2️⃣ 단순 CASE식
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
✔️ COALESCE : 하나씩 조회해서 null이 아니면 반환
select coalesce(m.username,'이름 없는 회원') from Member m
➡️ 사용자 이름이 없으면 이름 없는 회원을 반환
✔️ NULLIF : 두 값이 같으면 null 반환, 다르면 첫번째 값 반환
select NULLIF(m.username, '관리자') from Member m
➡️ 사용자 이름이 ‘관리자’면 null을 반환하고 나머지는 본인의 이름을 반환
JPQL이 지원하는 표준 함수로 데이터베이스에 관계없이 그냥 쓰면 됨!
CONCAT
SUBSTRING
TRIM
LOWER
, UPPER
LENGTH
LOCATE
ABS
, SQRT
, MOD
SIZE
, INDEX
(JPA 용도)select function('group_concat', i.name) from Item i