[JPA] JPQL - 기본 문법(프로젝션, 페이징, 조인, 서브쿼리, 기타식, 함수)

·2024년 4월 21일
0

JPA

목록 보기
13/17
post-thumbnail

💡프로젝션

  • SELECT 절에 조회할 대상을 지정하는 것
  • 조회한 대상은 JPA가 영속성 컨텍스트로 관리한다
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
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 // 스칼라 타입 프로젝션

📗여러 값 조회

//meber 저장
Member member = new Member();
member.setName("member1");
member.setAge(10);
em.persist(member);

// 1.Query 타입으로 조회
List resultList1 = em.createQuery("select m.name, m.age from Member m")
    				.getResultList();

Object o = resultList1.get(0);
Object[] result1 = (Object[])o;
System.out.println("username = " + result1[0]); 
System.out.println("age = " + result1[1]); 

// 2.Object[] 타입으로 조회
List<Object[]> resultList2 = em.createQuery("select m.name, m.age from Member m")
      						.getResultList();

Object[] result2 = (Object[])o;
System.out.println("username = " + result2[0]); // username = member
System.out.println("age = " + result2[1]); // age = 29

// 3.new 명령어로 DTO로 조회
List<MemberDTO> resultList3 = em.createQuery(
        "select new jpabook.jpashop.dto.MemberDTO(m.name, m.age) from Member m",
        MemberDTO.class)
    .getResultList();
    1. Query 타입으로 조회
    1. Object[] 타입으로 조회
    1. new 명령어로 조회
    • 단순 값을 DTO로 바로 조회
    • 패키지 명을 포함한 전체 클래스 명 일벽
    • 순서와 타입이 일치하는 생성자 필요

💡페이징 API

  • JPA는 페이징을 다음 두 API로 추상화함
  • setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
  • setMaxResults(int maxResult) : 조회할 데이터 수
  • persistence의 dialect에 설정된 db에 맞게 페이징 SQL을 작성해준다.
em.createQuery("select m from Member m order by m.age desc", Member.class)
	.setFirstResult(0)
	.setMaxResults(10)
	.getReulstList();
/*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절

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.name

💡서브쿼리

ex)나이가 평균보다 많은 회원

select m from Member m
where m.age > (select avg(m2.age) from Member m2);

ex)한 건이라도 주문한 고객

select m from Member m
where (select count(o) from Order o where m=o.member)>0

📗서브쿼리 지원 함수

  • [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 where t.name='팀A')

/*전체 상품 각각의 재고보다 주문량이 많은 주문들*/
select o from Order o
where o.orderAmout > ALL (select p.stockAmount from Product p)

/*어떤 팀이든 팀에 소속된 회원*/
select m from Member m
where m.team = ANY(select t from Team t)

📗JPA 서브 쿼리 한계

  • JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
  • SELECT 절도 가능(하이버네이트에서 지원)
  • FROM 절의 서브쿼리는 현재 JPQL에서 불가능
    • 조인으로 풀 수 있으면 풀어서 해결

📌하이버네이트6 변경사항
하이버네이트6 부터는 FROM 절의 서브쿼리를 지원한다!

💡JPQL 타입 표현과 기타식

JPQL 타입 표현과 기타식

📗JPQL의 타입 표현

  • 문자 : 'HELLO', 'SHE''S'
  • 숫자 : 10L(Long), 10D(Double), 10F(Float)
  • Boolean : TRUE, FALSE
  • ENUM : jpabook.MemberType.Admin (패키지명 포함)
 	selet m.username, 'HELLO', true from Member m
    where m.type = jpql.MemberType.ADMIN
  • 엔티티 타입 : TYPE(m) = Member (상속관계에서 사용)
 	em.createQuery("select i from Item i where type(i) = Book", Item.class);

📗JPQL 기타

  • SQL과 문법이 같은 식
  • EXISTS, IN
  • AND, OR, NOT
  • =, >, >=, <, <=, <>
  • BETWEEN, LIKE, IS NULL

💡조건식

  • CASE식

📗기본 CASE 식

	//나이가 10보다 작거나 같으면 학생요금, 60보다 크거나 같으면 경로요금, 둘다 아니면 일반요금 출력
	select case when m.age <= 10 then '학생요금'
				when m.age >= 60 then '경로요금'
				else '일반요금'
				end
	from Member m

📗단순 CASE 식

	//팀 이름이 팀A면 인센티브 110%, 팀B면 인센티브 120%, 둘다 아니면 인센티브 105% 출력
	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 반환, 다르면 첫번째 값 반환
	//사용자 이름이 `관리자`면 null을 반환하고 나머지는 본인 이름을 반환
	select NULLIF(m.username, '관리자') from Member ;

💡JPQL 기본 함수

  • CONCAT
  • SUBSTRING
  • TRIM
  • LOWER, UPPER
  • LENGTH
  • LOCATE
  • ABS, SQRT, MOD
  • SIZE INDEX(JPA용도)
//- CONCAT
select concat('a','b'); //ab

//- SUBSTRING: firstParam의 값을 secondParam위치부터 thirdParam갯수만큼 잘라서 반환
select substring('abcd', 2,3) // bc

//- TRIM
select trim(' lee han sol ')//lee han sol

//- LOWER, UPPER
select LOWER('hansolHI');//hansolhi
select UPPER('hansolHI');//HANSOLHI

//- LENGTH
select LENGTH('hansolHI'); // 6

//- LOCATE
select LOCATE('so', 'hansol');//4

//- ABS, SQRT, MOD
select ABS(-30);// 30
select SQRT(4);//2
select MOD(4,2);//0

//- SIZE, INDEX(JPA용도)
select SIZE(t.members) from Team t // 0 

💡사용자 정의 함수 호출

  • 하이버네이트는 사용전 방언에 추가해야한다.
    • 사용하는 DB방언을 상속받고, 사용자 정의 함수를 등록한다
//group_concat이라는 함수를 만들어서 등록한다고 가정한다.
public class MyPostgresDialect extends PostgreSQL94Dialect {
    public MyPostgresDialect() {
        registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
    }
...
...

//설정파일 등록
<property name="hibernate.dialect" value="jpql.MyPostgresDialect"/>
profile
백엔드 개발자를 꿈꿉니다 / 이전 블로그 : https://po-dadak.tistory.com/category

0개의 댓글

관련 채용 정보