-
JPA (9) - JPQL (기본문법)카테고리 없음 2024. 1. 12. 12:13
> jpql은 단순하게 조회가 가능하고, 객체 그래프 탐색으로 쿼리를 날릴 수 있다는 장점이 있다!
> JPA는 엔티티 객체 중심 개발이므로, SQL또한 테이블이 아닌 엔티티 객체를 대상으로 검색하도록 돕는다.
> SQL을 추상화했다. (문법 매우 유사하다) -> JPQL은 엔티티 객체를 대상으로 쿼리를 한다.
*모든 DB 데이터를 객체로 변환하는 것은 불가능하다 -> 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된
SQL이 필요하다.
String jpql = "select m From Member m where m.name like '%hello%'"; List<Member> result = em.createQuery(jpql, Member.class).getResultList();
1. JPQL 기본 문법과 기능
"select m from Member as m where m.age >18" select COUNT(m), SUM(m.age), AVG(m.age), MAX(m.age), MIN(m.age) from Member m
> 엔티티와 속성은 대소문자 구분한다!!
> JPQL 키워드 대소문자X (SELCT ,FROM)
> 엔티티 이름을 사용한다 (테이블 명이 아니다!)
> 별칭은 필수 (as 생략 가능)
> GROUP BY, HAING, ORDER BY 사용 가능
1.1 TypeQuery, Query
- TypeQuery: 반환 타입이 명확할 때 사용
- Query : 반환 타입이 명확하지 않을 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m",Member.class); Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
- 결과 조회
query.getResultList() : 결과가 하나 이상 -> 결과가 없으면 빈 리스트 반환
query.getSingleResult() : 결과가 정확히 하나, 단일 객체 반환
(결과 없으면: javax.persistence.NoResultException, 둘 이상이면 javax.persistence.NonUniqueResultException)
1.2 파라미터 바인딩
SELECT m FROM Member m where m.username =:username query.setParameter("username",usernameParam);
> 위치 기반 바인딩도 있는데 그건 쓰지말자
1.3 프로젝션
> 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 // 스칼라 타입 프로젝션 //엔티티 타입 프로젝션 -> 결과 영속성 관리 List<Member> selectMember = em.createQuery("select m from Member m where m.username=:username", Member.class) .setParameter("username",member.getUsername()) .getResultList(); Member findMember = selectMember.get(0); findMember.setAge(20); //Member안에 Team 조회 m.team으로 해도 묵시적 join발생 혹은 select m.team from Member (join Team t on m.MEMBER_ID = t.MEMBER_ID) //하지만 묵시적 조인은 추후에 알아보기 힘들기 때문에 명시적으로 조인을 써주자 List<Member> selectMember2 = em.createQuery("select t from Member m join m.team t", Member.class).getResultList();
> DISTINCT로 중복 제거 가능
> 프로젝션으로 여러 값 조회(여러 스칼라와 같이)
SELECT m.username, m.age FROM Member m
-> Query 타입으로 조회
Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
-> Object[] 타입으로 조회 (결과 하나당 Object[]배열에 담겨나옴)
List<Object[]> objectResult = em.createQuery("SELECT m.username, m.age FROM Member m").getResultList(); for(Object[] result : objectResult){ //..result[0],result[1] }
-> new 명령어로 조회 (DTO 사용)
List<MemberDTO> results = em.createQuery("SELECT new jpql.MemberDTO(m.username,m.age) from Member m",MemberDTO.class) .getResultList();
> 패키지명 포함한 전체 클래스명 입력, 순서와 타입이 일치하는 생성자 필요하다.
> 해당 데이터를 자주 조회하는 경우 위 방식을 고려해보자
2. 페이징 API
> JPA는 페이징을 다음 두 API로 추상화
setFirstResult(int startPosition) : 조회 시작 위치
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();
3. 조인
내부조인 : SELECT m FROM Member m [INNER] JOIN m.team t;
외부조인 : SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
세타 조인: SELECT count(m) FROM Meber m, Team t where m.username = t.name
> ON 절을 활용한 조인
- 조인 대상 필터링
- 연관관계 없는 엔티티 외부조인
//회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인 SELECT m,t FROM Member m LEFT JOIN m.team t on t.name = 'A' // 약간 on 하기 전에 조건넣기 SELECT m,t FROM Member m LEFT JOIN Team t on m.username = t.name //연관관계 없는 열 조인
4. 서브쿼리
//나이가 평균보다 많은 회원 (서브쿼리랑 메인쿼리 관련 없음 -> 성능상 좋다) 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
-> 서브 쿼리 지원 함수 (EXSIST,ALL,ANY,SOM,IN 지원)
//팀A 소속인 회원 SELECT m FROM Member m WHERE exsist(select t from m.team t where t.name ='팀A'); //m.team이면 조인해서 가져옴 //전체 상품 각각의 재고보다 주문량이 많은 주문들 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)
> JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능 (SELECT 절은 하이버네이트 지원, FROM도 하이버네이트)
5. JPQL 타입 표현
> 문자 : 'HELLO', 'SEH''s' // '표현하고 싶으면 ''두개
> 숫자 : 10(LONG), 10D(Double), 10F(Float)
> Boolean : TRUE, FALSE
> ENUM : jpabook.MemberType.Admin (패키지명 포함)
> 엔티티타입: TYPE(m) = Member(상속관계에서 사용)
//ENUM타입 String query="select m.username,'HELLO',TRUE From Member m WHERE m.type = jpql.MemberType.ADMIN"; String query="select m.username,'HELLO',TRUE From Member m WHERE m.type = :type"; List<Object[]> result = em.createQuery(query).setParameter("type",MemberType.ADMIN).getResultList(); 일케 바꾸기 가능 setPArameter //상속관계에서 Item을 뽑는데 조건으로 자식 사용 em.createQuery("select i from Item i where type(i) = Book");
> CASE식
SELECT case when m.age <= 10 then '학생요금' case when m.age >= 60 then '경로요금' else '일반요금' end from Member m SELECT case t.name when '팀A' then //.. end FROM Team t select coalesce(m.username, '이름 없는 회원') from Member m //하나씩 조회해서 null이 아니면 반환 select NULLIF(m.username, '관리자') from Member m // 두 값이 같으면 null 다르면 첫번째 값 반환 //유저명이 관리자면 null 아니면 유저명
> JPQL 기본 함수 (JPQL 표준 DB와 상관없이 사용가능)
- CONCAT, SUBSTRING, TRIM, LOWER, UPPER, LENGTH, LOCATE(IndexOf),ABS,SQRT,MOD
- SIZE,INDEX(JPA 용도)
* 사용자 정의 함수 호출 가능 -> 사용자 정의함수 만드는 방법은 필요시 찾아보자..
select function('group_concat',i.name) from Item i //사용자 정의함수 호출