[JPA] 서브쿼리, 타입표현과 기타식, CASE식, 함수
📌 Topic
- 💡 서브 쿼리(Sub Query)
- 💡 JPQL 타입 표현과 기타식
- 💡 조건식 - CASE 식
- 💡 JPQL 함수
⚡ 01. 서브 쿼리(Sub Query)
쿼리 안에 또 다른 쿼리를 작성하는 것
01-1. 나이가 평균보다 많은 회원
select m from Member m
where m.age > (select avg(m2.age) from Member m2)
01-2. 한 건이라도 주문한 회원
select m from Member m
where (select count(o) from Order o where m = o.member) > 0
01-3. 서브쿼리 지원 함수
EXISTS가 IN에 비해 성능 면에서 뛰어나다.
EXISTS : 메인 쿼리 -> 서브쿼리 연산
IN : 서브쿼리 -> 메인 쿼리 연산
- [NOT] EXISTS (subquery) : 서브쿼리에 결과가 존재하면 참(true)
-
{ALL ANY SOME} (subquery) - ALL 모두 만족하면 참(true)
- ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참(true)
-
- [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참(true)
01-4. 서브쿼리 예제
https://lhoris.tistory.com/136
--팀 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)
01-5. JPA 서브 쿼리 한계
// 스칼라 서브 쿼리
String sql = "select (select avg(m1.age) from Member m1) as avgAge from Member m";
TypeQuery<Member> member = em.createQuery(sql, Member.class).getResultList();
// 인라인뷰
String sql = "select mm.age, mm.username from (select m.age, m.username from Member m) as mm";
// 중략
- JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용이 가능하다.
- SELECT 절도 가능(하이버네이트 지원).
- FROM 절의 서브 쿼리는 현재 JPQL에서 불가능하다.
- 조인으로 풀 수 있으면 풀어서 해결한다.
- FROM 절에서 사용하고 싶은 경우 Native SQL을 사용해야 한다.
⚡ 02. JPQL 타입 표현과 기타식
- 문자 : ‘HELLO’, ‘She”s’
- 숫자 : 10L(Long), 10D(Double), 10F(Float)
- Boolean : TRUE, FALSE
- ENUM : jpabook.MemberType.Admin(패키지명 포함)
- 엔티티 타입 : TYPE(m) = Member(상속 관계에서 사용)
02-1. JPQL 기타
기본적으로 표준 SQL은 다 지원한다
- SQL과 문법이 같은 식
- EXISTS, IN
- AND, OR, NOT
- =, >, >=, <, <=, <>
- BETWEEN, LIKE, IS NULL
⚡ 03. 조건식 - CASE 식
기본적으로 RDBMS에서 사용하는 SQL과 비슷한 것 같다.
03-1. 기본 CASE 식
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
03-2. 단순 CASE 식
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
03-3. JPQL CASE 식 예제
try {
JpqlTeam team = new JpqlTeam();
team.setName("teamA");
em.persist(team);
JpqlMember member = new JpqlMember();
member.setUsername("teamA");
member.setAge(10);
member.setType(MemberType.USER);
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
String query =
"select " +
"case when m.age <= 10 then '학생요금' " +
" when m.age >= 60 then '경로요금' " +
" else '일반요금' " +
"end " +
"from JpqlMember m";
List<String> resultList = em.createQuery(query, String.class).getResultList();
for (String s : resultList) {
System.out.println("s = " + s);
}
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
03-4. 조건식 - CASE 식
--사용자 이름이 없으면 이름 없는 회원을 반환
select coalesce(m.username, '이름 없는 회원') from Member m
- COALESCE: 하나씩 조회해서 null이 아닌면 반환.
--사용자 이름이 '관리자'면 null을 반환하고 나머지는 본인의 이름을 반환
select NULLIF(m.username, '관리자') from Member m
- NULLIF: 두 값이 같으면 null 반환, 다르면 첫 번째 값 반환.
try {
JpqlTeam team = new JpqlTeam();
team.setName("teamA");
em.persist(team);
JpqlMember member = new JpqlMember();
// member.setUsername("teamA");
// member.setUsername(null); // coalesce 테스트
// member.setUsername("관리자"); // NULLIF 테스트 => null 반환 해야함
member.setUsername("졸리다"); // NULLIF 테스트 => null 반환 해야함
member.setAge(10);
member.setType(MemberType.USER);
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
// 01. case 식 사용
String query =
"select " +
"case when m.age <= 10 then '학생요금' " +
" when m.age >= 60 then '경로요금' " +
" else '일반요금' " +
"end " +
"from JpqlMember m";
List<String> resultList1 = em.createQuery(query, String.class).getResultList();
// 02. coalesce : 하나씩 조회해서 null이 아니면 반환
String query2 = "select coalesce(m.username, '이름 없는 회원') from JpqlMember m";
List<String> resultList2 = em.createQuery(query, String.class).getResultList();
// 03. NULLIF : 두 값이 같으면 null 반환, 다르면 첫 번째 값 반환
String query3 = "select NULLIF(m.username, '관리자') from JpqlMember m";
List<String> resultList3 = em.createQuery(query, String.class).getResultList();
for (String s : resultList3) {
System.out.println("s = " + s);
}
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
- Java 소스 내에서 해당 표준 함수 테스트.
⚡ 04. JPQL 기본 함수
- CONCAT
- SUBSTRING
- TRIM
- LOWER, UPPER
- LENGTH
- LOCATE
- ABS, SORT, MOD
- SIZE, INDEX(JPA 용도)
04-1. Concat
// 01. concat -> 문자열 붙히기
String query = "select concat('a', 'b') from JpqlMember m";
String query2 = "select 'a' || 'b' from JpqlMember m";
04-2. Substring
// 02. substring -> 문자열 위치 기반 자르기
String query3 = "select substring(m.username, 2, 3) from JpqlMember m";
04-3. Trim
// 03. trim -> 공백 제거
String query4 = "select trim(m.username) from JpqlMember m";
04-4. Locate
// 04. locate -> indexOf랑 비슷한 듯
String query5 = "select locate('de', 'abcdefg') from JpqlMember m";
04-5. size
// 05. size -> collection의 크기를 반환 한다.
String query6 = "select size(t.memberList) from JpqlTeam t";
04-6. index
// 06. index -> 사용 안하는게 좋음, 값 타입 컬렉션의 위치값을 반환 할 때 사용
String query7 = "select index(t.memberList) from JpqlTeam t";
04-7. 사용자 정의 함수
// 07. 사용자 정의 함수 등록
String query8 = "select function('group_concat', m.username) from JpqlMember m";
List<String> resultList = em.createQuery(query8, String.class).getResultList();
- 하이버네이트 사용전 방언에 추가해야 한다.
- 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.
댓글남기기