[Spring] JPQL 정리1

신범철·2023년 9월 22일
0

스프링부트

목록 보기
13/20

JPA에서 쿼리를 날리는 방법

  • JPQL
    • SQL과 유사한 객체 중심의 쿼리 언어
    • Spring Data JPA에서 정해놓은 네이밍 컨벤션을 지키면 적절한 JPQL을 구성해준다.
  • QueryDSL
    • SQL과 같은 쿼리를 생성하는 빌더(JPQL의 단점인 동적쿼리 등을 커버하기 위한 용도로 사용)
  • JPA Criteria
    • JPA에서 표준으로 지원하는 SQL생성 빌더(동적 쿼리 가능, 복잡해서 잘 사용하진 않는다)
  • Native SQL
    • 실제 SQL를 사용할 수 있는 기능 제공
  • JDBC API 직접 사용

JPA Criteria

  • JPA에서 표준으로 등록되어 있지만 잘 사용되진 않는다.
  • 대안으로 QueryDSL를 사용한다.
//Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);

//루트 클래스 (조회를 시작할 클래스)
Root<Member> m = query.from(Member.class);

//쿼리 생성 CriteriaQuery<Member> cq =
query.select(m).where(cb.equal(m.get("username"), “kim”));
List<Member> resultList = em.createQuery(cq).getResultList();

QueryDSL

  • 문자가 아닌 Java코드로 JPQL을 작성한다.
  • JPQL 빌더
  • 컴파일 시점에서 오류를 찾을 수 있다.
  • 설정하는 과정이 필요하다.
  • 동적쿼리 사용이 편리하다.
  • 실무에서 많이 사용된다.
JPAFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;

List<Member> list =
  query.selectFrom(m)
    .where(m.age.gt(18))
    .orderBy(m.name.desc())
    .fetch();

Native SQL

  • JPQL에서 실제 SQL을 사용하는 방법이다.
  • JPQL과 QueryDSL로 해결할 수 없는 기능을 구현할 때 사용된다.
String sql =SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = ‘kim’";

List<Member> resultList =
          em.createNativeQuery(sql, Member.class).getResultList();     

JDBC 직접 사용

  • JPQL과 QueryDSL로 해결할 수 없는 기능을 구현할 때 사용된다.
  • 영속성 컨텍스트를 적절한 시점에 강제로 flush()해줘야 한다.
    • Why? -> JPA와 관련이 없기 때문에 JPA 로직상 영속성 컨텍스트를 통해 영속화를 통해 바로 실제 DB에 접근하지 않기 때문에 올라른 로직 수행을 위해 적절한 flush()가 필요하다.

JPQL

  • JPQL은 Entity를 대상으로 사용하는 객체지향 쿼리 언어이다.
  • 일반적인 SQL은 테이블이 중심이지만, JPQL은 객체 중심이다.
  • SQL과 유사한 문법을 가지고 있고, ANSI 표준 기능을 제공한다.
  • SQL을 추상화해서 특정 DB에 의존하지 않는다.

JPQL 기본 규칙

  • SQL과 유사한 문법
select_문 =
  select_절
  from_절
  [where_절]
  [groupby_절]
  [having_절]
  [orderby_절]

update_문 = update_절 [where_절]
delete_문 = delete_절 [where_절]
  • Entity와 속성은 대소문자 구분해야 한다.(Member, age)
  • JPQL 키워드는 대소문자 구분할 필요없다.(SELECT, from)
  • 테이블 이름이 아닌 Entity 이름을 사용한다.
  • 별칭은 필수로 작성해야 한다.
select m from Member as m where m.age > 18

JPQL 사용법

  • 집합
select
  COUNT(m) // 회원 수
  SUM(m.age) // 나이 합
  AVG(m.age) // 평균 나이
  MAX(m.age) // 최대 나이
  MIN(m.age) // 최소 나이
from Member m
  • 정렬
select
  m from
Member m
group by m.name // 이름으로 그룹
order by m.id // 정렬은 id 순서
  • TypeQuery : 반환타입이 명확할 때 사용한다.
 /* 반환 타입이 정확히 Member 클래스 --> 반환 클래스 파라미터로 지정! */
TypedQuery<Member> query =
   em.createQuery("SELECT m FROM Member m", Member.class);
  • Query : 반환 타입이 명확하지 않을 때 사용한다.
 /* 반환 타입이 정확히 Member 클래스 --> 2번째 파라미터 없음 */
Query query =
   em.createQuery("SELECT m.name, m.age FROM Member m");
  • 쿼리 결과 조회 API

    • query.getResultList() : 결과가 하나 이상일 때, 리스트를 반환한다. 결과가 없는 경우 -> 빈리스트 반환한다.
    • query.getSingleResult() : 결과가 하나일 때, 단일 객체를 반환하다.
      결과가 없는 경우 -> 오류 javax.persistence.NoResultException
      (추후 java 8의 Optional로 처리 가능)
      결과가 둘 이상인 경우 -> 오류 Javax.persistence.NonUniqueResultException
  • 파라미터 바인딩

    • 파라미터 바인딩 방법 : :변수명 + setParameter()를 사용한다.
    • 이름 기준 : 변수 이름을 기준으로 바인딩한다.
    • 위치 기준 : 파라미터 위치를 기준으로 바인딩한다.
/* 이름 기준 파라미터 바인딩 -- 이름으로 바인딩 위치를 찾음 */
em.createQuery("select m from Member m where m.name=:name", Member.class)
       .setParameter("name","범철").getResultList();

/* 위치 기준 파라미터 바인딩 -- 파라미터 위치 순서에 맞게 지정 */
em.createQuery("select m from Member m where m.name=?1", Member.class)
       .setParameter(1,"범철").getResultList();

JPQL 페이징

  • 특정 데이터의 구역을 나눠서 가져오는 것을 페이징이라고 한다.
  • JPA 사용으로 매우 간편하게 사용가능하다.
  • 페이징 메서드
    • setFirstResult(int startPosition)
      : 조회 시작 위치(0부터 시작)
    • setMaxResult(int maxResult)
      : 조회할 데이터 수
//페이징 쿼리
String jpql = "select m from Member m order by m.name desc";
List<Member> resultList = em.createQuery(jpql, Member.class)
    .setFirstResult(10) // 11번째부터 (0부터 시작이니까)
    .setMaxResults(20) // 20개를 가져와라
    .getResultList();

[ DB별 페이징시 실제 쿼리 ]

  • 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_ > ?

JPQL JOIN

  • inner 조인
/* inner join */
List<Member> innerJoin = em.createQuery
        ("select m from Member m inner join m.team t", Member.class)
        .getResultList();

/* 결과 */
Hibernate:
        select
            생략 ..
        from
            Member member0_
        inner join
            Team team1_
                on member0_.team_id=team1_.team_id
  • outer 조인
/* outer join */
List<Member> leftOuterJoin = em.createQuery
        ("select m from Member m left join m.team t", Member.class)
        .getResultList();

/* 결과 */
Hibernate:
        select
            생략 ..
        from
            Member member0_
        left outer join
            Team team1_
                on member0_.team_id=team1_.team_id

[ on 절을 활용한 조인 ]

  • on절은 JPA 2.1버전 부턴 지원한다.
  • 조인 대상 필터링
    • 조인을 하기 전 조인할 대상을 미리 필터링한다.
  • 연관관계가 없는 엔티티 외부 조인(hibernate 5.1 버전부터 지원)
    • 서로 연관관계가 없는 엔티티끼리 외부 조인이 가능하다.
/* 조인 대상 필터링 */
List<Member> resultList = em.createQuery
   ("select m from Member m left join m.team t on t.teamName = m.name", Member.class)
   .getResultList();

// 같은 쿼리를 where로 했을 때
List<Member> resultList = em.createQuery
  ("seelct m from Member m left join m.team t where t.teamName = m.name", Member.class)
  .getResultList();

on절 뒤에 조건을 주가해서 대상을 필터링한다.

/* 연관관계가 없는 엔티티 조인 */
List<Member> result = em.createQuery
  ("select m from Member m left join User u on m.name = u.name", Member.class)
  .getResultList();

on절 뒤에 추가적인 조건으로 join을 명시한다.

profile
https://github.com/beombu

0개의 댓글