본문 바로가기

백엔드/JPA

JPQL 기초

JPQL 소개

JPA가 지원하는 쿼리 방법에는 여러가지가 있다. JPQL, queryDSL, JPA Criteria, 네이티브 SQL. JPQL은 이 방법들 중 하나이며 엔티티를 대상으로 쿼리를 날린다는 특징을 가지고 있다.  (참고로 Criteria는 공부할 일도, 쓸 일도 없을 듯하다)

JPQL을 한마디로 정의하면 객체지향 SQL이다.

//jpql 예시
List<Team> resultList1 = em.createQuery("select t from Team t join t.members", Team.class).getResultList();

이렇게 엔티티를 대상으로 쿼리를 날리기때문에 디비에 있는 컬럼명으로 쓰지않고 그냥 클래스의 필드 이름으로 쿼리를 날릴 수 있는 매우 편한 도구이다. jpql의 결과를 가져오는 함수는 두가지이다. 위의 예시에 있는 getResultList() 와 getSingleResult()이다.

  • getResultList : 결과가 여러개일때
  • getSingleResult : 결과가 딱 한개일때!! (그 외엔 에러발생)

JPQL 사용법에 대해..

em.createQuery("select m from Member as m where m.age > 24");

- JPQL은 엔티티와 필드 입력시 대소문자를 구분한다. (이거땜에 30분 날림)
- 테이블 이름이 아닌 엔티티의 이름을 사용한다.
- 별칭은 필수다

em.createQuery("SELECT m FROM Member m where m.username=:username");
query.setParameter("username", usernameParam);

동적으로 쿼리 생성시에 위처럼 파라미터를 넣어줄 수도 있다.

JPQL은 물론 프로젝션도 가능하다. 원하는 항목만 select 해올 수 있다.

em.createQuery("select m.age, m.name from Member as m where m.age > 24");

만약 위처럼 가져오려 할 때에는 DTO를 만들어서 하는 것이 가장 좋은 방법이다.

List<MemberDTO> resultList = em.createQuery("select new jpql.MemberDTO(m.username, m.age) 
\ from Member m", MemberDTO.class).getResultList();

JPQL 쿼리 안에서 엔티티를 직접 사용하면 자동으로 기본키 값을 이용하게 된다.

select m from Member m where m =: member
select m from Member m where m.id =: memberId

둘의 결과는 동일하다.

 

페이징, 서브쿼리, case식

JPQL에서는 다음과 같이 페이징이 가능하다.

for(int i=0;i<100;i++){
                Member member = new Member();
                member.setUsername("sdfa"+i);
                member.setAge(i);
                em.persist(member);
            }
            em.flush();
            em.clear();

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

            for (Member member : resultList) {
                System.out.println("member = " + member.getAge());
            }
select m from Member m where m.age > (select avg(m2.age) from Member m2)

그리고 이런 서브쿼리도 가능하다. 하지만 FROM절의 서브쿼리는 지원하지 않는다.

	// CASE 식 예시
    
		Member member = new Member();
        member.setUsername("lsh");
        member.setAge(15);
        em.persist(member);

        Member member2 = new Member();
        member.setAge(8);
        em.persist(member2);

        em.flush();
        em.clear();

        String query = "select case when m.age <= 10 then '학생요금' when m.age >= 11 then '성인요금' end from Member m";
        String query2 = "select coalesce(m.username , '없는 이름') from Member m";
        List<String> resultList = em.createQuery(query, String.class)
                                     .getResultList();
        for (Object o : resultList) {
            System.out.println(o);
        }

        List<String> resultList1 = em.createQuery(query2, String.class)
                                     .getResultList();
        for (Object o : resultList1) {
            System.out.println(o);
        }

 

JPQL 기본함수

  • CONCAT
  • SUBSTRING
  • TRIM
  • LOWER , UPPER
  • LENGTH
  • LOCATE
  • ABS, SORT, MOD
  • SIZE, INDEX (JPA 전용 함수)

size는 예를 들면

이런식으로 해당 컬렉션에 몇개 있는지. 그니까 이 팀에 멤버 몇명인지 알려준다. index는 값타입 컬럼에서 대상의 위치값 찾을때 @OrderColumn 라는 어노테이션이 있는데 이것과 함께 사용한다. (하지만 이건 권장 X)

 

 

 

관련글

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 를 공부하며 정리한 글입니다.

'백엔드 > JPA' 카테고리의 다른 글

JPQL 페치조인의 한계, 다양한 쿼리  (0) 2021.08.09
JPQL 중급문법  (0) 2021.08.08
JPA 값타입  (0) 2021.08.08
JPA 프록시와 영속성 전이  (0) 2021.08.06
JPA 엔티티 매핑2  (0) 2021.08.05