RE-Heat 개발자 일지

[JPA] [10] 객체지향 쿼리 언어 - 기본 문법 (하편) 본문

백엔드/JPA

[JPA] [10] 객체지향 쿼리 언어 - 기본 문법 (하편)

RE-Heat 2023. 8. 17. 23:13

https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

초급자를 위해 준비한 [웹 개발, 백엔드] 강의입니다. JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자

www.inflearn.com

인프런 김영한 님의 강의를 듣고 작성한 글입니다.

 

[4] 페이징

JPA는 DB마다 다른 페이징 문법을 두 가지 API로 추상화했다.
  • setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
  • setMaxResults(int maxResult) : 조회할 데이터 수

JpaMain

List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
					.setFirstResult(1)
					.setMaxResults(10)
					.getReulstList();

  • serFirstResult(1)로 지정해서 index 1부터 시작(DESC라 내림차순임)
  • setMaxResults(10)으로 지정해 10개의 결괏값이 조회됨.

 

DB 방언 별로 알아서 쿼리문을 날려준다. 매우 편리하다.

 

 

[5] 조인

■ 내부조인

SELECT m FROM Member m [INNER] JOIN m.team t

 

예시]

① Member와 Team 다대일 연관관계 매핑(양방향)

    ※주의 @ManyToOne에 fetch= FetchType.LAZY로 지연 로딩으로 지정해 주는 걸 잊으면 안 된다.

② JpaMain - query : "select m from Member m inner join m.team t"

    inner는 생략 가능

 

■ 외부조인

SELECT m FROM Member m LEFT [OUTER] JOIN m.team t

예시]

① JpaMain - query : "select m from Member m left join m.team t

 

② 참고]  query = "select m from Member m left join m.team t where t.name = :teamName"

=> :로 파라미터 바인딩도 추가

 

■ 세타조인

select count(m) from Member m, Team t where m.username = t.name

=> 연관성이 없는 두 테이블을 조인하는 방법

 

예시]

JpaMain - query : "select m from Member m, Team t where m.username = t.name"

 

■ 조인 - on 절

1. 조인 대상 필터링

예) 회원과 팀을 조인하면서, 팀 이름이 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. 연관 관계없는 엔티티 외부 조인(하이버네이트 5.1부터)

예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인

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;

 

[6] 서브 쿼리

예시 1] 나이가 평균값보다 많은 회원

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

=> 메인쿼리의 Member m과 서브쿼리의 Member m2를 따로 정의. 이렇게 코드를 짜야 성능이 잘 나온다.

 

예시 2] 한 건이라도 주문한 고객

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): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

 

예제

  • 팀 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)

 

■ JPA 서브쿼리의 한계(=> 패치로 변경)

하이버네이트 6부터 FROM절의 서브쿼리를 지원한다.

 

 

[7] JPQL 타입 표현과 기타식

■ JPQL 타입 표현

  • 문자: ‘HELLO’, ‘She’’s’

        => ' 표현하려면 '' 두 번 넣으면 된다.

  • 숫자: 10L(Long), 10D(Double), 10F(Float)
  • Boolean: TRUE, FALSE
  • ENUM: jpabook.MemberType.Admin (패키지명 포함)

우측은 파라미터 형식, 우측 방식이 좀 더 명확하다.

주의점 : @Enumerated(EnumType.STRING) 빼먹지 말자. 

=> DB엔 enum이 없어서 꼭 이렇게 지정해줘야 한다.

=> ENUM은 자바 패키지명까지 넣어야 하는 걸 잊지 말자.

  • 엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)

 

■ JPQL 기타

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

예시 1] IS NOT NULL

 

예시 2] BETWEEN

 

[8] 조건식(CASE 등등)

■ 기본 CASE 식

select 
	case when m.age <= 10 then '학생요금'
			when m.age >= 60 then '경로요금'
			else '일반요금'
	end
from Member m

주의 : end를 빼먹지 말자

나이는 10으로 세팅되어 있다.

 

■ 단순 CASE 식

select
	case t.name
		when '팀A' then '인센티브110%' 
		when '팀B' then '인센티브120%' 
		else '인센티브105%'
	end 
from Team t

 

■ COALESCE

하나씩 조회해서 null이 아니면 반환

String query = "select coalesce(m.username, '이름 없는 회원') from Member m";

null이면 '이름 없는 회원' 반환

 

■ NULLIF: 두 값이 같으면 null 반환, 다르면 첫 번째 값 반환

String query = "select nullif(m.username, '관리자') as username from Member m";

관리자와 일치하니 null값 반환

 

[9] JPQL 함수

• CONCAT

 

왼쪽은 CONCAT 말고 사용

• SUBSTRING

2번째부터 세 글자 "memberA" => "emb"


• TRIM : 공백 제거
• LOWER, UPPER : 소문자화, 대문자화
• LENGTH : 길이
• LOCATE

'de'는 'abcdefg'의 네 번째부터 시작된다는 뜻이다.

• ABS, SQRT, MOD : 절댓값, 루트(제곱근), 나머지
• SIZE, INDEX(JPA 용도)

String query = "select size(t.members) From Team t";

team의 size가 몇 개인지 알려준다. 여기선 따로 persist를 하지 않아 0이 나옴.

 

■ 사용자 정의 함수

  • 하이버네이트는 사용 전 방언에 추가해야 한다.
  • 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.

 

MyH2Dialect

H2Dialect를 상속받은 후 함수를 추가

 

persistence.xml

 

JpaMain

String query = "select group_concat(m.username) From Member m";