@Query 사용

이진욱·2024년 12월 25일

이론

목록 보기
18/24

@Query 는 Spring Data JPA에서 사용되는 애노테이션으로, 리포지토리 인터페이스에서 사용자 정의 쿼리를 작성할 때 사용됩니다. 이는 기본 제공 메서드(findById, save 등)로 처리할 수 없는 복잡한 쿼리를 정의하거나, 특정 데이터베이스 기능을 활용해야 할 때 유용합니다.

주요 특징

  • JPQL 또는 네이티브 쿼리 사용 가능

  • 기본적으로 JPQL(Java Persistence Query Language)을 사용하지만, 네이티브 SQL을 작성할 수도 있습니다.
    네이티브 쿼리를 사용할 경우 nativeQuery = true 속성을 설정해야 합니다.

  • 메서드 서명과 매핑

    • @Query 애노테이션의 쿼리 결과는 메서드 반환 타입과 매핑됩니다.
    • 반환 타입으로는 엔티티, DTO, 컬렉션, 또는 특정 값(예: String, Integer)을 사용할 수 있습니다.
  • 파라미터 바인딩

    • 파라미터는 위치 기반(?1, ?2) 또는 이름 기반(:parameterName)으로 바인딩할 수 있습니다.

1. 기본 JPQL 예제

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 특정 이름을 가진 사용자 검색
    @Query("SELECT u FROM User u WHERE u.name = :name")
    List<User> findUsersByName(@Param("name") String name);
}

2. 네이티브 쿼리 사용

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 네이티브 쿼리로 특정 조건의 사용자 검색
    @Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
    User findUserByEmail(@Param("email") String email);
}

3. 동적 값 바인딩

@Query("SELECT u FROM User u WHERE u.age > :minAge AND u.age < :maxAge")
List<User> findUsersByAgeRange(@Param("minAge") int minAge, @Param("maxAge") int maxAge);

4. 위치 기반 파라미터로 @Query 작성

  1. 리포지토리 인터페이스
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 위치 기반 파라미터로 이름과 이메일로 사용자 검색
    @Query("SELECT u FROM User u WHERE u.name = ?1 AND u.email = ?2")
    Optional<User> findByNameAndEmail(String name, String email);
}
  1. 서비스 계층
@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Optional<User> getUserByNameAndEmail(String name, String email) {
        return userRepository.findByNameAndEmail(name, email);
    }
}
  1. 컨트롤러 계층
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/search")
    public ResponseEntity<User> getUser(@RequestParam String name, @RequestParam String email) {
        return userService.getUserByNameAndEmail(name, email)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }
}
설명
  • 쿼리 작성

?1?2첫 번째(name)두 번째(email) 메서드 매개변수로 자동 매핑됩니다.

  • 호출 순서

매개변수는 작성한 순서대로 위치 기반 바인딩됩니다. ?1은 findByNameAndEmail의 첫 번째 매개변수, ?2두 번째 매개변수에 매핑됩니다.

  • HTTP 요청 예제

GET /users/search?name=John&email=john@example.com

  • 결과

사용자가 존재하면 HTTP 200 응답과 함께 사용자 정보가 반환됩니다.
존재하지 않으면 HTTP 404 응답이 반환됩니다.

장점과 단점
  • 장점:
    단순한 쿼리에서 간결하게 사용 가능.
    매개변수 이름을 명시하지 않아도 되므로 빠르게 작성 가능.
  • 단점:
    매개변수 순서가 변경되면 쿼리가 의도와 다르게 동작할 수 있음.
    복잡한 쿼리에서는 가독성이 떨어질 수 있음.

추가 옵션

@Modifying: 데이터 수정 쿼리(INSERT, UPDATE, DELETE)를 실행할 때 사용합니다.

@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateUserStatus(@Param("status") String status, @Param("id") Long id);

@Transactional: 쿼리가 트랜잭션 내에서 실행되도록 보장합니다. 읽기 전용 쿼리에는 @Transactional(readOnly = true)를 사용할 수 있습니다.

주의사항

네이티브 쿼리 사용 시 데이터베이스 종속성

네이티브 쿼리는 특정 DBMS에 종속될 가능성이 있으므로, 이식성을 고려해야 합니다.

결과 매핑

DTO와 같이 엔티티가 아닌 반환 타입을 사용할 경우 결과를 명시적으로 매핑해야 할 수 있습니다.
예를 들어, JPQL에서 new 키워드를 사용해 DTO 객체를 생성합니다.

쿼리 최적화

지나치게 복잡한 쿼리는 성능에 영향을 줄 수 있으므로, 필요시 적절한 인덱스와 캐싱을 고려해야 합니다.
@Query는 강력한 커스터마이징 도구이지만, 작성한 쿼리를 유지보수하기 쉽게 관리하는 것이 중요합니다. 쿼리 작성 시, 비즈니스 요구사항과 성능을 모두 고려하여 설계하세요.

profile
열심히 하는 신입 개발자

0개의 댓글