개인공부/JPA

객체지향 쿼리언어 1 - 기본문법

j_yeaseul 2023. 4. 6. 21:05

JPQL 소개

  • JPQL 은 엔티티 객체를 대상으로 쿼리를 하고 SQL은 데이터베이스 테이블을 대상으로 쿼리를 날린다. 
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다. 
  • JPQL은 결국 SQL로 변환된다.
List<Member> result = em.createQuery(
	"select m From Member m where m.username like '%kim%'",Member.class
    ).getResultList();
    
    tx.commit();

JPQL 문법

  • select  m from Member as m where m.age > 18
  • 엔티티와 속성은 대소문자를 구분한다. 
  • JPQL 키워드는 대소문자를 구분안한다. (select, FROM, where..)
  • 엔티티 이름을 사용한다 (테이블 이름이 아님)
  • 별칭은 필수(m) (as는 생략가능)


 

TypeQuery와 Query

//반환타입이 분명할 때
TypeQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypeQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);

//반환타입이 다수이거나 불분명함
Query query3 = em.createQuery("select m.username, m.age from Member m");

결과조회 API

  • query.getResultList() : 결과가 하나 이상일 때 리스트 반환 -> 결과가 없으면 빈리스트 반환 -> nullpointException 상관안해도됨
  • query.getSingleResult() : 결과가 정확히 하나일때. 단일객체를 반환
  • - 결과가 없으면 : javax.persistence.NoResultException
  • - 둘 이상이면 : javax.persistence.NonUniqueResultException
TypeQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = query.getResultList();

for(Member member : resultList) {
	System.out.println(member);
}

TypeQuery<Member> query2 = em.createQuery("select m from Member m where m.id ='100' ", Member.class);
Member result = query.getSingleResult();
System.out.println(result);

파라미터 바인딩 - 이름기준, 위치기준

//이름기준
Member result = em.greateQuery("select m from Member m where m.username=:username", Member.class)
.setParameter("username","member1")
.getSingleResult();

System.out.println("result = "+result.getUsername());


//위치기준(왠만하면 사용하지 말것(밀림))
sember result = em.greateQuery("select m from Member m where m.username=?1", Member.class)
.setParameter(1,"member1")
.getSingleResult();

프로젝션

  • 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 -> 스칼라 타입 프로젝션
  • distinct 로 중복제거 가능
List<Member> result = em.greateQuery("select m from Member m" , Member.class)
.getResultList();

//Member와 Team테이블을 조인해서 결과값을 가져옴.
List<Team> result = em.greateQuery("select m.team from Member m" , Team.class)
.getResultList();

 

프로젝션 - 여러값을 조회해 올땐..?

  • select m.username, m.age from Member m
  • 1. query 타입으로 조회
  • 2. Object[]타입으로 조회
  • 3. new  명령어로 조회
  •  - 단순값을  DTO로 바로 조회 (select new jpabook.jpql.UserDTO(m.username, m.age) from Member m
  • 패키지 명을 포함한 전체 클래스명을 입력한다. 
  • 순서와 타입이 일치하는 생성자가 필요하다. 
public class MemberDTO {
	private String username;
    private int age;
    
    public MemberDTO(String username, int age){
    	this.username = username;
        this.age = age;
    }
    
    //getter, setter 생략
}
List<MemberDTO> resultList = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();

MemberDTO memberDTO = result.get(0);
System.out.println(memberDTO.getUsername());
System.out.println(memberDTO.getAge());

페이징 API

  • JPA는 페이징을 다음 두 API로 추상화함.
  • setFirstResult(int startPosition) : 조회 시작위치
  • setMaxResults(int maxResult) : 조회할 데이터 수
String jpql = "select m from Member m order by m.age desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
SELECT * FROM MEMBER M
ORDER BY M.AGE DESC LIMIT ?,?

조인

: innerjoin - SELECT M FROM Member m [INNER] JOIN m.team t

: outerjoin - SELECT M FROM Member m LEFT [OUTER] JOIN m.team t 

: selectjoin - SELECT count(m) from Member m, Team t where m.username= t.name

 

//innerjoin
String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class).getResultList();

//crossjoin
String query = "select m from Member m, Team t where m.username = t.name";
List<Member> result = em.createQuery(query, Member.class).getResultList();

조인 - 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 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

서브쿼리

  • 나이가 평균보다 많은 회원
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
  • 팀 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)

 

서브쿼리 지원함수

  • [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
  • {All | ANY | SOME} (subquery)
  • ALL 모두 만족하면 참
  • ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참
  • [NOT] IN (subquery) : 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

 

JPA 서브쿼리의 한계

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

JPQL 타입표현

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

//보통 바인딩해서 아래처럼 사용한다. 
String query = "select m.username, 'HELLO', true from Member m
where m.type = :userType"
List<Object[]> result = em.createQuery(query)
.setParameter("userType", MemberType.ADMIN)
.getResultList();

//상속관계
em.createQuery("select i from Item i where type(i) = Book",Item.class)
.getResultList();

조건식 - CASE식

[ 기본 CASE식] - 조건에 부합하면 결과를 내는 식

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

[단순 CASE식] - 정확한 매칭

select
	case t.name 
    	when '팀A' then '인센티브110%'
    	when '팀B' then '인센티브120%'
        else '인센티브105%'
    end
from Team t
  • COALESCE : 하나씩 조회해서 null이 아니면 반환한다. 
select coaleace(m.username, '이름없는 회원') from Member m
  • NULLIF : 두 값이 같으면 null 반환 다르면 첫째값 반환
select nullif(m.username, '관리자' ) from Member m

JPQL 기본 함수

  • CONCAT : 문자를 더함
  • SUBSTRING : 문자를 자름
  • TRIM : 공백제거 같은거
  • LOWER, UPPER : 대소문자
  • LENGTH : 길이
  • LOCATE 
select locate('de','abcdef') from Member m  //4
  • ABS, SQRT, MOD
  • SIZE, INDEX(JPA용도)
//컬렉션에 대한 크기를 돌려줌
select size(t.members) From Team t

사용자 정의 함수 호출

: 하이버네이트는 사용 전 방언에 추가해야 한다. 

: 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다. 

: (참고) mysql이면 MySQL57Dialect 클래스에 이미 정의되어있는 사용자 정의 함수들이 많음.

public class MYH2Dialect extends H2Dialect {
	public MyH2Dialect() {
	registerFunction("group_concat", new StandardFunction('group_concat',StandardBasicTypes.STRING));
    }
    }

 

'개인공부 > JPA' 카테고리의 다른 글

UPDATE: 변경감지와 병합(merge)  (0) 2023.05.23
객체지향 쿼리언어 2- 중급문법  (0) 2023.04.12
값타입  (0) 2023.04.02
영속성 전이 : CASCADE  (0) 2023.03.30
즉시 로딩과 지연 로딩  (0) 2023.03.30