@Query 는 Spring Data JPA에서 사용되는 애노테이션으로, 리포지토리 인터페이스에서 사용자 정의 쿼리를 작성할 때 사용됩니다. 이는 기본 제공 메서드(findById, save 등)로 처리할 수 없는 복잡한 쿼리를 정의하거나, 특정 데이터베이스 기능을 활용해야 할 때 유용합니다.
JPQL 또는 네이티브 쿼리 사용 가능
기본적으로 JPQL(Java Persistence Query Language)을 사용하지만, 네이티브 SQL을 작성할 수도 있습니다.
네이티브 쿼리를 사용할 경우 nativeQuery = true 속성을 설정해야 합니다.
메서드 서명과 매핑
@Query 애노테이션의 쿼리 결과는 메서드 반환 타입과 매핑됩니다.파라미터 바인딩
위치 기반(?1, ?2) 또는 이름 기반(:parameterName)으로 바인딩할 수 있습니다.@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);
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 네이티브 쿼리로 특정 조건의 사용자 검색
@Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
User findUserByEmail(@Param("email") String email);
}
@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);
@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);
}
@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);
}
}
@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는 두 번째 매개변수에 매핑됩니다.
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는 강력한 커스터마이징 도구이지만, 작성한 쿼리를 유지보수하기 쉽게 관리하는 것이 중요합니다. 쿼리 작성 시, 비즈니스 요구사항과 성능을 모두 고려하여 설계하세요.