✏️ [JPQL] Named query

박상민·2023년 11월 2일
0

JPA

목록 보기
20/24
post-thumbnail

JPQL 쿼리는 크게 동적 쿼리와 정적 쿼리로 나눌 수 있다.

동적 쿼리

  • 동적 쿼리는 em.createQuery(”select ..”)와 같이 JPQL을 문자로 완성해서 직접 넘기는 것을 동적 쿼리라 한다.

  • 런타임에 특정 조건에 따라 JPQL을 동적으로 구성할 수 있다.

Name Query(정적 쿼리)

  • Name 쿼리는 미리 정의한 쿼리에 이름을 부여하여 필요할 때 사용할 수 있다.
  • Named 쿼리는 한 번 정의하면 변경할 수 없는 정적인 쿼리이다.

⭐️ Named query - 정적 쿼리

  • Named 쿼리는 미리 정의해서 이름을 부여해두고 사용하는 JPQL이다.
    • 한 번 정의하면 변경할 수 없는 정적인 쿼리
  • @NamedQuery 어노테이션 또는 XML 문서에 정의할 수 있다.
  • 애플리케이션 로딩 시점에 초기화 후 재사용한다.
  • 애플리케이션 로딩 시점에 JPQL 문법을 검증한다.
    • JPQL 문법 오류가 있으면 애플리케이션 실행시 에러가 발생한다.

Named 쿼리를 어노테이션에 정의

@Entity
@NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member m where m.username = :username"
)
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private int age;

    ...
}

Named 쿼리 사용

List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
        .setParameter("username", "회원1")
        .getResultList();

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

Named 쿼리 환경에 따른 설정

  • XML로 설정된 Named 쿼리가 우선권을 가진다.
  • 애플리케이션 운영 환경에 따라 다른 XML을 배포할 수 있다.

Spring Data JPA에서의 Named 쿼리

Spring Data JPA에서는 Named 쿼리를 인터페이스 메서드에 바로 선언할 수 있다.

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m from Member m where m.emailAddress = ?1")
    Member findByEmailAddress(String emailAddress);
}

간편하게 설정할 수 있으며, Named 쿼리의 장점을 모두 가지고 있다.

@Query

SpringDataJPA를 사용하면 Repository method에 @Query Annotation을 사용해서 위의 과정을 SpringDataJPA에 위임할 수 있다.

@Query(name = "User.findByUsername")
List<User> findByUsername(@Param("username") String username);

앞에서는 Entity에 @NamedQuery라는 애노테이션 안에서 쿼리문을 명시해서 사용했다. 그래서 Entity에 쿼리가 많이 쌓인다는 단점이 있고, Entity가 쿼리까지도 담당하게 되어서 단일 책임 원칙도 벗어나게 된다.

그래서 SpringDataJPA는 엔티티에서 @NamedQuery를 사용하지 않고 Repository method에서 쿼리를 바로 정의할 수 있게 @Query를 제공한다.

@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
}

@Query("select u from User u where u.username = :username")
List<User> findUser(@Param("username") String username);
NamedQuery vs @Query

NamedQuery vs @Query
그럼 어느쪽을 사용하는 것이 바람직할까?

NamedQuery 사용시 파라미터가 3개 이상이 되거나 order by 절 등을 사용해서 쿼리가 복잡해지면 메서드명이 엄청나게 복잡해져서 가독성이 저하된다.

이럴때는 @Query를 사용하는 것이 좋다. 실제로 실무에서도 아주 많이 사용한다고 한다.

필요한 부분만 꺼내쓰자!
추가적으로 주의할 사항은 필요한 부분만 꺼내쓰는 것이다. Entity 조회시 Hibernate 캐시에 복사본 저장, 불필요한 컬럼 조회, OneToOne에서의 N+1 쿼리 등 단순 조회 기능에서는 성능 이슈 요소가 많다.

따라서 영속성 컨텍스트에서 Entity를 관리할 필요가 없다면 DTO를 사용하는 것이 바람직하다.

마무리

JPA가 좋은 이유가 있는데, 예를 들어 username이라는 필드 대신에 실수로 usernaem이라고 오타를 쳤다고 가정하자.

Node.js의 ORM을 사용하면, 서버에서 해당 리포지토리 메소드가 실행되었을 때 비로소 결함을 발견할 수 있다.

위 방식을 사용하면 애플리케이션을 로딩할 때 @Query의 쿼리문을 파싱을 해서 미리 만들어둔다. 그래서 파싱하는 과정에서 문제를 인식하게 되고, 런타임에러가 발생해서 서버 자체가 올라가지 않는다.

Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException:
  could not resolve property: usernaem of: study.datajpa.entity.User [select u from study.datajpa.entity.User m where m.usernaem = :username]

출처
자바 ORM 표준 JPA 프로그래밍 강의
게시글 속 자료는 모두 위 강의 속 자료를 사용했습니다.

profile
스프링 백엔드를 공부중인 대학생입니다!

0개의 댓글