HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
Spring Data
Spring Data
/
🧣
JPA(Java Persistence API)
/
🏰
@Query
🏰

@Query

Use Case #1 : JPQL, Parameter mappingUse case #2 : @Query 결과를 interface 혹은 구체클래스에 매핑Native Query @Query(nativeQuery=true)QueryDSL참고
 
  • queryMethod의 커스텀 버전
  • queryMethod의 가독성 문제
    • 함수이름이 되게 길어지는 문제
  • Entity에서 전체가 아닌 필요한 필드들만 추출해서 조회가 가능함
  • nativeQuery를 쓰게 되면 QueryDSL을 주로 사용함

Use Case #1 : JPQL, Parameter mapping

  • @Query의 value 안에 들어있는 쿼리를 JPQL이라고 함. JPA의 Entity를 기반으로 하는 쿼리
    • Book, createdAt, updatedAt 등, Entity 안에서 정의된 변수 이름임
  • 쿼리메소드로 만드는 쿼리와 비슷한 과정을 통해서 쿼리가 만들어짐(JPQL)
  • JPQL 안에 파라미터를 넣는 방법
    • ?1, ?2 와 같이 순서대로 매핑하는 방법
      • 그러나 순서에 의한 파라미터 넘김은 자바에서 지양하고 있다고 함 ← 언제 파라미터가 추가될지 모르니까
    • @Param을 이용한 파라미터 연결

Use case #2 : @Query 결과를 interface 혹은 구체클래스에 매핑

Native Query @Query(nativeQuery=true)

  • JPQL 처럼 Entity를 이용하지 못함. 실제 raw query임
  • dialect를 활용하지 않기에 특정 db에 맞게 쿼리 작성 해야함 → 최소한으로 사용하는 것이 좋음
  • 사용 이유?
    • 성능에 대한 이슈 : JpaRepository를 활용한 update는 항상 search후 save를 통해 merge로 업데이트 됨
    • 일반적인 jpql 에서 사용할 수 없는 쿼리(show databases)를 사용할 수 있음
    • @Modifying

    • 해당 쿼리 메소드가 modifying query(INSERT, UPDATE, DELETE) 혹은 DDL 일 때, 붙여주는 annotation임
    • clearAutomatically : JPQL 실행 후 자동으로 영속성 컨텍스트 비워줌 → db에서 다시 가져오기에 캐쉬로 인한 데이터 불일치가 발생하지 않음
      • 그러나 항상 붙이는게 좋은 것만은 아님. 왜냐하면 업데이트를 조회랑 같이 사용하는게 아닌 따로 사용한다면 굳이 persistence context를 비워주는 작업을 할 필요가 없는 것임
    • @Modifying Query는 Transaction이 꼭 필요함. @Transactional로 트랜잭션 안에 포함되지 않으면 아래의 에러가 발생

      Native Query 이용시 dto projection 방법

      @SqlResultSetMapping과 @NamedNativeQuery 이용
      StackOverFlow — Mapping jpa or hibernate projection query to DTO @Query

      위치 기반 파라미터 바인딩에서 list 바인딩

      StackOverFlow — JPA Passing list to in clause in native query
 

QueryDSL참고

https://querydsl.com/
public interface BookRepository extends JpaRepository<Book, Long>{ List<Book> findByCategoryIsNullAndNameEqualsAndCreatedAtGreaterThanEqualAndUpdatedAtGreaterThanEqual( String name, LocalDateTime createdAt, LocalDateTime updatedAt); @Query(value="select b from Book b " + "where name=?1 and createdAt >= ?2 and updatedAt >= ?3 and category is null") List<Book> findByNameRecently(String name, LocalDateTime createdAt, LocalDateTime updatedAt); @Query(value="select b from Book b " + "where name = :name and createdAt >= :createdAt and updatedAt >= :updatedAt and category is null") List<Book> findByNameRecently2( @Param("name") String name, @Param("createdAt") LocalDateTime createdAt, @Param("updatedAt") LocalDateTime updatedAt); }
@Query(value="select b.name as name, b.category as category from Book b") List<BookNameAndCategory> findBookNameAndCategory(); public interface BookNameAndCategory { String getName(); String getCategory(); } //test code bookRepository.findBookNameAndCategory().forEach( b -> System.out.println(b.getName() + " : " + b.getCategory()));
인터페이스에 매핑
@Query(value="select new com.example.bookmanager.repository.dto.BookNameAndCategory(b.name, b.category) from Book b") List<BookNameAndCategory> findBookNameAndCategory(); @Data @NoArgsConstructor @AllArgsConstructor public class BookNameAndCategory { private String name; private String category; }
구체 클래스에 매핑
List<Book> books = bookRepository.findAll(); for(Book book : books){ book.setCategory("IT전문서"); } bookRepository.saveAll(books); // 대량 데이터에 대해 update 시 성능 저하. id 값으로 하나씩 update 해야하기에 // -> 이럴 때 native query사용 @Transactional @Modifying @Query(value = "update book set category = 'IT전문서'", nativeQuery = true) int updateCategory();
javax.persistence.TransactionRequiredException: Executing an update/delete query