JPQL 소개
- JPQL 은 엔티티 객체를 대상으로 쿼리를 하고 SQL은 데이터베이스 테이블을 대상으로 쿼리를 날린다.
- SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
- JPQL은 결국 SQL로 변환된다.
List<Member> result = em.createQuery(
"select m From Member m where m.username like '%kim%'",Member.class
).getResultList();
tx.commit();
JPQL 문법
- select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자를 구분한다.
- JPQL 키워드는 대소문자를 구분안한다. (select, FROM, where..)
- 엔티티 이름을 사용한다 (테이블 이름이 아님)
- 별칭은 필수(m) (as는 생략가능)
TypeQuery와 Query
//반환타입이 분명할 때
TypeQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypeQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
//반환타입이 다수이거나 불분명함
Query query3 = em.createQuery("select m.username, m.age from Member m");
결과조회 API
- query.getResultList() : 결과가 하나 이상일 때 리스트 반환 -> 결과가 없으면 빈리스트 반환 -> nullpointException 상관안해도됨
- query.getSingleResult() : 결과가 정확히 하나일때. 단일객체를 반환
- - 결과가 없으면 : javax.persistence.NoResultException
- - 둘 이상이면 : javax.persistence.NonUniqueResultException
TypeQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = query.getResultList();
for(Member member : resultList) {
System.out.println(member);
}
TypeQuery<Member> query2 = em.createQuery("select m from Member m where m.id ='100' ", Member.class);
Member result = query.getSingleResult();
System.out.println(result);
파라미터 바인딩 - 이름기준, 위치기준
//이름기준
Member result = em.greateQuery("select m from Member m where m.username=:username", Member.class)
.setParameter("username","member1")
.getSingleResult();
System.out.println("result = "+result.getUsername());
//위치기준(왠만하면 사용하지 말것(밀림))
sember result = em.greateQuery("select m from Member m where m.username=?1", Member.class)
.setParameter(1,"member1")
.getSingleResult();
프로젝션
- 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 로 중복제거 가능
List<Member> result = em.greateQuery("select m from Member m" , Member.class)
.getResultList();
//Member와 Team테이블을 조인해서 결과값을 가져옴.
List<Team> result = em.greateQuery("select m.team from Member m" , Team.class)
.getResultList();
프로젝션 - 여러값을 조회해 올땐..?
- select m.username, m.age from Member m
- 1. query 타입으로 조회
- 2. Object[]타입으로 조회
- 3. new 명령어로 조회
- - 단순값을 DTO로 바로 조회 (select new jpabook.jpql.UserDTO(m.username, m.age) from Member m
- 패키지 명을 포함한 전체 클래스명을 입력한다.
- 순서와 타입이 일치하는 생성자가 필요하다.
public class MemberDTO {
private String username;
private int age;
public MemberDTO(String username, int age){
this.username = username;
this.age = age;
}
//getter, setter 생략
}
List<MemberDTO> resultList = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
MemberDTO memberDTO = result.get(0);
System.out.println(memberDTO.getUsername());
System.out.println(memberDTO.getAge());
페이징 API
- JPA는 페이징을 다음 두 API로 추상화함.
- setFirstResult(int startPosition) : 조회 시작위치
- setMaxResults(int maxResult) : 조회할 데이터 수
String jpql = "select m from Member m order by m.age desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
SELECT * FROM MEMBER M
ORDER BY M.AGE DESC LIMIT ?,?
조인
: innerjoin - SELECT M FROM Member m [INNER] JOIN m.team t
: outerjoin - SELECT M FROM Member m LEFT [OUTER] JOIN m.team t
: selectjoin - SELECT count(m) from Member m, Team t where m.username= t.name
//innerjoin
String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class).getResultList();
//crossjoin
String query = "select m from Member m, Team t where m.username = t.name";
List<Member> result = em.createQuery(query, Member.class).getResultList();
조인 - ON절
- 조인대상 필터링
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 t
on m.team_id = t.id
and t.name='A'
- 연관관계 없는 엔티티 외부조인
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.name
서브쿼리
- 나이가 평균보다 많은 회원
select m from Member m
where m.age>(select avg(m2.age) from Member m2)
- 한 건이라도 주문한 고객
select m from Member m
where (select count(o) from Order o where m = o.member) > 0
- 팀 A 소속인 회원
select m from Member m
where exists(select t from m.team t where t.name='팀A')
- 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o
where o.orderAmount > ALL(select p.stockAmount from Product p)
- 어떤 팀이든 팀에 소속된 회원
select m from Member m
where m.team = ANY(select t from Team t)
서브쿼리 지원함수
- [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
- {All | ANY | SOME} (subquery)
- ALL 모두 만족하면 참
- ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참
- [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
JPA 서브쿼리의 한계
- JPA는 WHERE, HAVING 절에서만 서브쿼리 사용이 가능하다.
- SELECT 절도 가능
- FROM 절의 서브쿼리는 현재 JPQL에서 불가능 (조인으로 풀 수 있으면 풀어서 해결함)
JPQL 타입표현
- 문자 : 'HELLO' , 'SHE''S'
- 숫자 : 10L(Long), 10D(Double), 10F(Float)
- Boolean : TRUE, FALSE
- ENUM : jpabook.MemberType.Admin (패키지명 포함)
- 엔티티 타입 : TYPE(m) = Member (상속관계에서 사용)
String query = "select m.username, 'HELLO', true from Member m
where m.type = jpql.MemberType.ADMIN";
List<Object[]> result = em.createQuery(query).getResultList();
//보통 바인딩해서 아래처럼 사용한다.
String query = "select m.username, 'HELLO', true from Member m
where m.type = :userType"
List<Object[]> result = em.createQuery(query)
.setParameter("userType", MemberType.ADMIN)
.getResultList();
//상속관계
em.createQuery("select i from Item i where type(i) = Book",Item.class)
.getResultList();
조건식 - CASE식
[ 기본 CASE식] - 조건에 부합하면 결과를 내는 식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
[단순 CASE식] - 정확한 매칭
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
- COALESCE : 하나씩 조회해서 null이 아니면 반환한다.
select coaleace(m.username, '이름없는 회원') from Member m
- NULLIF : 두 값이 같으면 null 반환 다르면 첫째값 반환
select nullif(m.username, '관리자' ) from Member m
JPQL 기본 함수
- CONCAT : 문자를 더함
- SUBSTRING : 문자를 자름
- TRIM : 공백제거 같은거
- LOWER, UPPER : 대소문자
- LENGTH : 길이
- LOCATE
select locate('de','abcdef') from Member m //4
- ABS, SQRT, MOD
- SIZE, INDEX(JPA용도)
//컬렉션에 대한 크기를 돌려줌
select size(t.members) From Team t
사용자 정의 함수 호출
: 하이버네이트는 사용 전 방언에 추가해야 한다.
: 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.
: (참고) mysql이면 MySQL57Dialect 클래스에 이미 정의되어있는 사용자 정의 함수들이 많음.
public class MYH2Dialect extends H2Dialect {
public MyH2Dialect() {
registerFunction("group_concat", new StandardFunction('group_concat',StandardBasicTypes.STRING));
}
}
'개인공부 > JPA' 카테고리의 다른 글
UPDATE: 변경감지와 병합(merge) (0) | 2023.05.23 |
---|---|
객체지향 쿼리언어 2- 중급문법 (0) | 2023.04.12 |
값타입 (0) | 2023.04.02 |
영속성 전이 : CASCADE (0) | 2023.03.30 |
즉시 로딩과 지연 로딩 (0) | 2023.03.30 |