Using JPA Named Queries

2024. 10. 20. 18:13Spring Boot/Spring Data JPA

JPA Named Queries 사용하기: @NamedQuery 어노테이션

JPA에서 Named Queries는 @NamedQuery 어노테이션을 사용하여 정의할 수 있으며, 코드에서 직접 쿼리를 설정할 수 있는 장점이 있습니다. Named Query는 쿼리 이름을 통해 호출되므로, 코드의 가독성을 높이고 중복을 줄이는 데 효과적입니다.

Named Query 정의하기

Named Query는 주로 엔티티 클래스에 정의되며, JPA 쿼리 언어(JPQL)를 사용하여 작성됩니다. 다음 예제를 통해 Named Query를 정의하는 방법을 살펴보겠습니다.

예제 1: 사용자 엔티티와 Named Query
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(name = "User.findByLastname", 
            query = "SELECT u FROM User u WHERE u.lastname = :lastname")
@NamedQuery(name = "User.findByActive", 
            query = "SELECT u FROM User u WHERE u.active = :active")
@NamedQuery(name = "User.findByAgeGreaterThan", 
            query = "SELECT u FROM User u WHERE u.age > :age")
public class User {
    @Id
    @GeneratedValue
    private Long id;

    private String firstname;
    private String lastname;
    private Integer age;
    private Boolean active;

    // Getters and setters
}
  • User.findByLastname: 성(lastname)으로 사용자를 찾는 쿼리입니다.
  • User.findByActive: 활성(active) 상태인 사용자를 찾는 쿼리입니다.
  • User.findByAgeGreaterThan: 주어진 나이(age)보다 큰 사용자를 찾는 쿼리입니다.

Repository 인터페이스에서 Named Query 사용하기

이제 UserRepository에서 이러한 Named Query를 사용할 수 있습니다.

예제 2: UserRepository 인터페이스
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

    // Named Query 사용 (Spring Data가 자동으로 해결)
    List<User> findByLastname(String lastname);

    // Named Query 사용 (명시적으로 호출)
    @Query(name = "User.findByActive")
    List<User> findUsersByActive(Boolean active);

    @Query(name = "User.findByAgeGreaterThan")
    List<User> findUsersByAgeGreaterThan(Integer age);
}
  • findByLastname: Spring Data는 메서드 이름을 기반으로 User.findByLastname named query를 자동으로 사용합니다.
  • findUsersByActive: @Query 어노테이션을 사용하여 활성 상태인 사용자 목록을 반환합니다.
  • findUsersByAgeGreaterThan: 주어진 나이보다 큰 사용자 목록을 반환합니다.

Named Query의 장점

  1. 중앙 집중화된 관리: 쿼리를 엔티티 클래스에 정의하므로, 쿼리 변경 시 엔티티 클래스에서만 수정하면 됩니다.
  2. 가독성: 쿼리 이름을 통해 어떤 쿼리가 호출되는지 쉽게 알 수 있습니다.
  3. 재사용성: 동일한 쿼리를 여러 곳에서 재사용할 수 있습니다.

추가 예제

다음은 다양한 Named Query를 사용한 예제입니다.

예제 3: 다양한 Named Query 정의
@Entity
@NamedQuery(name = "User.findByFirstnameAndLastname", 
            query = "SELECT u FROM User u WHERE u.firstname = :firstname AND u.lastname = :lastname")
@NamedQuery(name = "User.findDistinctByAge", 
            query = "SELECT DISTINCT u.age FROM User u")
@NamedQuery(name = "User.countByActive", 
            query = "SELECT COUNT(u) FROM User u WHERE u.active = :active")
public class User {
    // ... 필드 및 메서드
}
  • User.findByFirstnameAndLastname: 이름과 성으로 사용자를 찾는 쿼리입니다.
  • User.findDistinctByAge: 고유한 나이 목록을 반환합니다.
  • User.countByActive: 활성 사용자 수를 반환합니다.
예제 4: Repository에서 다양한 Named Query 사용하기
public interface UserRepository extends JpaRepository<User, Long> {

    // 이름과 성으로 사용자 찾기
    @Query(name = "User.findByFirstnameAndLastname")
    List<User> findUsersByFirstnameAndLastname(String firstname, String lastname);

    // 고유한 나이 목록 찾기
    @Query(name = "User.findDistinctByAge")
    List<Integer> findDistinctAges();

    // 활성 사용자 수 세기
    @Query(name = "User.countByActive")
    Long countActiveUsers(Boolean active);
}

이처럼 다양한 Named Query를 정의하고 Repository에서 사용할 수 있습니다. Named Query를 활용하면 쿼리를 재사용 가능하고 관리하기 쉬운 방식으로 작성할 수 있어 개발의 효율성을 높일 수 있습니다.

Named Query와 쿼리 메서드 관계

Named Query를 사용할 때 쿼리 메서드를 함께 정의하는 이유는 여러 가지가 있습니다. 이를 통해 Named Query와 Spring Data JPA의 기능을 조화롭게 활용할 수 있습니다. 아래에서 그 이유와 작동 방식을 자세히 설명하겠습니다.

1. Named Query와 쿼리 메서드의 역할

  • 쿼리 메서드는 Named Query를 호출하기 위한 래퍼 역할: 쿼리 메서드는 Named Query를 실행하기 위해 필요한 파라미터를 받아들이고, 실제 Named Query를 호출합니다. 이는 파라미터를 쿼리 문자열에 직접 바인딩하는 방식으로 처리됩니다. 즉, 쿼리 메서드는 Named Query에 아규먼트를 전달하는 역할을 합니다.
  • 가독성과 유지보수성 향상: Named Query를 정의하면 쿼리를 재사용할 수 있으며, 메서드 이름을 통해 쿼리의 의도를 명확하게 드러낼 수 있습니다. 이로 인해 코드의 가독성과 유지보수성이 높아집니다.

2. 메서드 이름에서 파생되는 쿼리

Spring Data JPA는 메서드 이름을 기반으로 자동으로 쿼리를 생성하는 기능을 제공합니다. 이 때의 규칙은 다음과 같습니다:

  • 쿼리 메서드가 Named Query를 호출하는 경우: 메서드 이름이 Named Query의 이름과 일치하면, Spring Data는 해당 Named Query를 자동으로 사용합니다. 예를 들어, findByLastname이라는 메서드는 @NamedQuery에서 정의한 User.findByLastname을 자동으로 호출하게 됩니다. 이는 메서드 이름에 의해 쿼리 구조를 명확히 하는 데 도움을 줍니다.
  • Named Query 사용 시 메서드 이름은 단순한 접근 포인트: Named Query를 사용하는 경우, 메서드 이름은 직접적인 쿼리 생성에 관여하지 않으며, Named Query가 이미 정의된 쿼리를 호출하는 역할만 합니다. 따라서 메서드 이름의 의도는 Named Query의 쿼리 아규먼트를 어떻게 처리할지를 명확히 하기 위한 것이며, 쿼리를 자동으로 생성하지는 않습니다.

3. 예시를 통한 이해

다음은 Named Query와 쿼리 메서드의 관계를 보여주는 예시입니다.

엔티티 클래스에서 Named Query 정의

@Entity
@NamedQuery(name = "User.findByLastname", 
            query = "SELECT u FROM User u WHERE u.lastname = :lastname")
public class User {
    // 필드 및 메서드
}

UserRepository 인터페이스

public interface UserRepository extends JpaRepository<User, Long> {

    // Named Query를 호출하는 메서드
    List<User> findByLastname(String lastname);
}

사용 예

@Autowired
private UserRepository userRepository;

public void example() {
    List<User> users = userRepository.findByLastname("Smith");
    // 이 호출은 User.findByLastname Named Query를 실행합니다.
}

Named Query 사용 시 쿼리 메서드 네이밍 제한 완화

쿼리 메서드의 이름 네이밍은 기본적으로 몇 가지 규칙을 따르지만, Named Query를 사용할 경우 네이밍에 대한 제한은 상대적으로 덜합니다. 여기서 몇 가지 포인트를 설명하겠습니다.

  • Named Query와의 관계: Named Query를 사용할 때, 메서드 이름은 Named Query의 이름과 매핑됩니다. 따라서 메서드 이름이 반드시 특정 형식을 따라야 할 필요는 없습니다. 예를 들어, findUsersByLastname과 같은 메서드 이름도 가능합니다. 하지만 이 메서드는 Named Query의 이름이 User.findByLastname과 매핑되기 때문에 메서드 이름이 Named Query의 이름과 일치하지 않아도 호출할 수 있습니다.
  • // Named Query와 관련이 없으므로 메서드 이름은 자유롭게 작성 가능 List<User> findUsersByLastname(String lastname);
  • 제한이 덜하다: Named Query를 사용할 경우 쿼리 메서드 이름은 제한이 덜하며, 자유롭게 설정할 수 있습니다.
  • 규칙과 일관성은 중요: 하지만, 가독성과 의도를 고려하여 규칙적인 네이밍을 하는 것이 좋습니다. 명확한 네이밍은 코드 유지보수와 협업에 큰 도움이 됩니다.

결론

  • 쿼리 메서드는 Named Query를 호출하는 래퍼: 이 메서드는 Named Query에 아규먼트를 전달하고, 결과를 반환받는 역할을 합니다.
  • 쿼리 메서드는 메서드 이름 기반으로 작동: 메서드 이름은 Named Query에 매핑되어 사용되며, 이름에 의해 쿼리가 자동으로 생성되거나 Named Query가 호출될 수 있습니다.
  • 가독성 및 유지보수성 향상: Named Query를 사용하면 쿼리를 중앙에서 관리할 수 있어 코드의 가독성과 유지보수성을 높일 수 있습니다.

따라서 Named Query와 쿼리 메서드는 서로 보완적인 관계에 있으며, Spring Data JPA의 강력한 기능을 활용하는 데 중요한 역할을 합니다.