-
JPA - JPQL 기본 문법과 기능Spring/JPA 2022. 3. 11. 14:56728x90반응형
JPQL 기본 문법과 기능
- JPA가 지원하는 다양한 쿼리
- JPQL 기본 문법
JPA가 지원하는 다양한 쿼리
JPQL
- 검색을 할 때 테이블이 아닌 엔티티 객체를 대상으로 검색
- 필요한 데이터만 DB에서 불러오기 위해 검색 조건이 포함된 SQL
- SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어(특정 DB SQL에 의존 X)
- SQL과 유사하며 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
- JPQL은 엔티티 객체를 대상으로 쿼리를 작성한다.
//검색 String jpql = "select m from Member m where m.age > 18"; List<Member> result = em.createQuery(jpql, Member.class) .getResultList();
실행된 SQL select m.id as id, m.age as age, m.USERNAME as USERNAME, m.TEAM_ID as TEAM_ID from Member m where m.age>18
Criteria
- 문자가 아닌 자바코드로 JPQL을 작성하는 방법
- JPQL 빌더 역할
- JPA 공식 기능
- 너무 복하고 실용성이 없어서 QueryDSL을 사용하는 것을 권장
//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();
QueryDSL
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있다.
- JPQL 빌더 역할
- 컴파일 시점에 문법 오류를 찾을 수 있다.
- 동적 쿼리 작성이 편리하다
- 단순하고 쉽다
- 실무에서 사용 권장!
//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();
JPQL 기본 문법
- 엔티티와 속성은 대소문자 구분을 해야 한다. (Member, age)
- JPQL 키워드는 대소문자 구분을 하지 않아도 된다. (SELECT, select)
- 테이블 이름이 아닌 엔티티 이름을 사용한다(예시 : Member)
- 별칭은 필수(as는 생략가능)
집합과 정렬
- COUNT, SUM, AVG, MAX, MIN 등 사용 가능
- GROUP BY, HAVING, ORDER BY 사용 가능
select COUNT(m), //회원수 SUM(m.age), //나이 합 AVG(m.age), //평균 나이 MAX(m.age), //최대 나이 MIN(m.age) //최소 나이 from Member m
반환 타입
- TypeQuery : 반환 타입이 명확할 때 사용
- Query : 반환 타입이 명확하지 않을 때 사용
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class); TypedQuery<String> query = em.createQuery("SELECT m.username FROM Member m", String.class); Query query = em.createQuery("SELECT m.username, m.age from Member m");
결과 조회
- query.getResultList() : 결과가 하나 이상일 때 리스트 반환
- 결과가 없을 시 : 빈 리스트 반환 - query.getSingleResult() : 결과가 정확히 하나인 단일 객체 반환
- 결과가 없을 시 : javax.persistence.NoResultException 예외 발생
- 결과가 둘 이상 : javax.persistence.NonUniqueResultException 예외 발생
List<Member> members = em.createQuery("SELECT m FROM Member m", Member.class) .getResultList(); List<String> usernames = em.createQuery("SELECT m.username FROM Member m", String.class) .getResultList(); Member member = em.createQuery("SELECT m from Member m.username = :username", Member.class) .setParameter("username", usernameParam) .getSingleResult;
파라미터 바인딩
- 이름 기준 : 파라미터 명을 기준으로 바인딩
- 위치 기준 : 파라미터 위치 기준으로 바인딩, 사용 권장 X
//이름 기준 Member member = em.createQuery("SELECT m from Member m.username = :username", Member.class) .setParameter("username", usernameParam) .getSingleResult; //위치 기준 Member member = em.createQuery("SELECT m from Member m.username = ?1", Member.class) .setParameter(1, usernameParam) .getSingleResult;
프로젝션(SELECT)
- SELECT 절에 조회할 대상을 지정하는 것
- 대상 : 엔티티, 임베디드 타입, 스칼라 타입(기본 데이터 타입)
- DISTINCT으로 중복을 제거할 수 있다.
-- 엔티티 프로젝션 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 -> 스칼라 타입 프로젝션
프로젝션(SELECT) - 여러 값 조회
- Query 타입으로 조회
- Object[] 타입으로 조회
- new 명령어로 조회
- 단순 값을 DTO로 바로 조회할 수 있다.
- 패키지 명을 포함한 전체 클래스 명 입력
- 순서와 타입이 일치하는 생성자 필요
- SELECT new 패키지명.클래스명(파라미터 명) FROM Member m
List<UserDTO> result = em.createQuery( "SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m", UserDTO.class)
페이징 API
- JPA는 페이징을 다음 두 API로 추상화되어 있다.
- setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
- setMaxResults(int maxResult) : 조회할 데이터 수
- 각 DB에 맞게 페이징 SQL을 만들어 준다.
//페이징 쿼리 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();
-- MySQL 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 ?, ? -- Oracle 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_ > ?
조인
- 내부 조인
- 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 Member m, Team t WHERE m.username = t.name
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 Team 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
서브 쿼리
- [NOT] EXISTS (subquery) : 서브쿼리에 결가가 존재하면 참
- { ALL | ANY | SOME } (subquery)
- ALL 모두 만족하면 참
- ANY, SOME : 같은 의미, 조건이 하나라도 만족하면 참 - [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
- WHERE, HAVING, SELECT 절에서 서브 쿼리 사용이 가능하다
- FROM 절의 서브 쿼리는 현재 JPQL에서 불가능하다
-- 팀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)
JPQL 타입 표현
- 문자 : 'hello', 'spring'
- 숫자 : 10L(Long), 10D(Double), 10F(Float)
- Boolean : TRUE, FALSE
- ENUM : jpabook.MemberType.ADMIN (패키지명 포함해야한다.)
(이 방법은 패키지명까지 넣어줘야 하므로 파라미터 바인딩으로 하면 편하다) - 엔티티 타입 : TYPE(m) = Member (상속 관계에서 사용)
ex) SELECT i FROM Item i WHERE type(i) = BOOK
조건식 - CASE
- CASE 조건식
select case when m.age <= 10 then '학생요금' when m.age >= 60 then '경로요금' else '일반요금' end from Member m select case t.name when '팀A' then '인센티브110%' when '팀B' then '인센티브120%' else '인센티브105%' end from Team t
- COALESCE : 하나씩 조회해서 NULL이 아니면 반환
- NULLIF : 두 값이 같으면 NULL 반환, 다르면 첫번째 값 반환
select coalesce(m.username,'이름 없는 회원') from Member m select NULLIF(m.username, '관리자') from Member m
JPQL 기본 함수
- CONCAT
- SUBSTRING
- TRIM
- LOWER, UPPER
- LENGTH
- LOCATE
- ABS, SQRT, MOD
- SIZE, INDEX
이외에도 사용자 정의 함수를 만들어 등록해주고 사용하는 방법이 있지만 지금은 내가 쓸일이 없을 것 같애서 생략...
728x90반응형'Spring > JPA' 카테고리의 다른 글
JPA - 설계 순서 (0) 2022.03.16 JPA - JPQL 기본 문법 2 (0) 2022.03.11 JPA - 값 타입(기본값, 임베디드, 불변 객체, 컬렉션 (0) 2022.03.10 JPA - 프록시와 연관관계(즉시 로딩, 지연 로딩, CASCADE) (0) 2022.03.10 JPA - 상속관계 매핑 (0) 2022.03.09