Spring Data JPA가 제공하는 쿼리 메서드는 조건 검색, 삭제 등 기본 JPA 가 제공하지 않는 기능들을 작성할 때 편리함을 제공하는 것을 이전 포스팅을 통해서 살펴보았는데요. 쿼리 메서드는 조인이나 복잡한 조건을 처리해야 하는 경우에는 이름이 너무나 길어지면서 불편함이 발생하기 시작합니다. 이런 특징 때문에 간단한 처리만 쿼리 메서드를 이용하고, 복잡한 경우에는 @Query 를 이용하는 경우가 더 많습니다.
@Query 의 Value : JPQL
@Query 의 value는 JPQL(Java Persistence Query Language)로 작성하는데, '객체지향 쿼리'라고 불리는 구문입니다. @Query 는 JPQL 문법에 맞추어서 작성을 해주어야 합니다. 아래 문법은 읽어만 보고 @Query 애노테이션 예시와 함께 보면 이해하기가 더욱 쉽습니다.
JPQL 의 문법은 다음과 같습니다. -) 테이블 대신 엔티티 클래스를 사용한다. -) 엔티티의 별칭을 사용한다. -) 쿼리에 필요한 값은 @Param 를 사용해서 함수의 인자로 선언한다. -) 파라미터는 " : 매개변수" 형태로 표현한다. -) nativeQuery = true 로 설정하면 일반 SQL을 그대로 사용할 수 있다. (여기서 사용하는 쿼리는 DBMS에 따라서 다르게 작성을 해주어야 한다.)
@Query 애노테이션 예시 (순수 쿼리 예시 포함)
어노테이션부터 살펴보겠습니다. 테이블 이름 대신에 엔티티 클래스 Memo 를 사용한 것을 볼 수 있습니다. 원래 테이블 이름은 "tbl_memo"였는데 Memo 를 사용했습니다. 같은 맥락으로 컬럼 명도 엔티티에 명시된 변수명을 사용해야 합니다. m.no 에서 사용된 no 가 이것에 해당합니다. 또한 엔티티 이름을 그대로 사용하지 않고 엔티티의 별칭을 설정하여 사용하는데요. Memo m 이라고 "m"이라는 별칭을 붙여준 것을 볼 수 있습니다. select m 에서 m 은 "*" 의 의미를 갖고 있습니다. 이 별칭은 조건식에서도 m.no 로 그대로 사용됩니다. 마지막으로 쿼리에 필요한 값은 @Param 애노테이션을 사용하여 매개변수로 선언하여야 합니다. m.no < :mno 에서 :mno 는 매개변수로 값을 매개변수로 받아오겠다는 것을 의미합니다. 이 매개변수를 @Param("mno") int mno; 이런 식으로 설정해주면 나중에 사용자가 값을 입력하면 값이 치환됩니다. 이번에는 순수 쿼리를 사용하는 예제를 보겠습니다.
nativeQuery = true 로 속성을 지정하면 순수 쿼리를 그대로 사용할 수 있게 됩니다. 순수 쿼리의 동작과정을 살펴보면 우선 순수 쿼리를 JPQL로 바꾼 다음에 JPQL이 실행되는 과정으로 실행됩니다. JPQL과 순수쿼리를 사용하는 것은 DBMS의 변동 가능성이 없다면 순수쿼리를 사용해도 되지만 그렇지 않다면 JPQL을 사용하는 것이 추천됩니다.
@Query 실습
MemoRepository 인터페이스에 위와 같이 @Query 애노테이션을 활용하여 쿼리를 작성해보았는데요. 위에 설명과 함께 보면 이해하기가 크게 어렵지는 않습니다. 위 4개의 메서드는 모두 이전에 MemoRepository2 에서 작성했던 쿼리메서드와 동일한 기능을 수행하는데요. @Query 애노테이션에서 활용되는 JPQL 을 작성할 때는 SQL 을 먼저 작성한 다음 문제가 없음을 확인하고 JPQL로 작성하는 것이 오류를 최소화할 수 있는 작성 방법입니다.
실제로 위에서 선언한 메서드 중에서 get3 를 사용해보겠습니다. MemoRepositoryTest3 이라는 테스트 파일을 생성하여서 get3() 메서드를 사용하는 것을 볼 수 있는데요. 출력 결과도 확인해보겠습니다.
출력 결과도 우리가 작성한 쿼리에 맞는 결과가 출력되고 있는 것을 확인할 수 있습니다.
@Query 애노테이션 연습문제 1. 순수 SQL
연습문제를 풀어볼건데요. 우선 BookRepository 에 구현한 코드부터 살펴보겠습니다.
@Query 어노테이션에서 순수 SQL을 사용할 때도 :bookName 처럼 파라미터를 받아올 수 있습니다. getBook1( ) 메서드에서 이런 점을 반영한 코드를 작성하였습니다.
BookRepositoryTest2 파일을 만들어서 위에서 작성한 메서드들을 위와 같이 사용해줄 수 있습니다.
@Query 애노테이션 연습문제 2. JPQL 사용
이번에는 GiftRepository2 파일부터 살펴보자.
JPQL을 작성하면서 실수했던 부분이 있는데, 테이블명을 적는 것이 아니라 여기서는 Entity 명을 적어주어야한다. 또한 SQL을 순수하게 작성하던 JPQL로 작성하던 Dbeaver 에서 SQL을 먼저 실행해보고 올바르게 작동하는지 확인을 한 다음 작성하여야 오류를 최소화 할 수 있다는 것을 잊지 말자.
실제로 테스트를 진행하는 GiftRepositoryTest2 파일을 만들고 작성한 메서드들을 사용해보았습니다. 모두 정상적으로 결과가 출력되는 것을 확인했습니다.
@Query 애노테이션 연습문제 3. JPQL 과 시간데이터검색
이번에는 Order Entity 부터 다시 살펴보고 진행을 하곘습니다.
Order Entity 에서 orderDate 멤버의 자료형은 LocalDateTime 임을 확인하고 진행을 하겠습니다.
우선 OrderRepository 인터페이스에 위와 같은 getOrder1( ) 메서드를 추가하였습니다. 파라미터로 자료형을 무엇을 해야 할지 고민이 될 수 있는데, Entity 에서 실제 자료형을 그대로 따라가면 됩니다.
테스트 파일을 만들어서 위와 같이 실행을 시키면 원하는 값을 확인할 수 있습니다.
@Query 애노테이션 연습문제 3. JPQL 과 시간데이터 범위검색
시간 검색 범위로 하고 싶을 때는 Date_Add 함수를 이용합니다. Interval 1 Day 처럼 쓸수도 있고 Interval 1 Month 로도 사용할 수 있습니다. 위 SQL을 그대로 JPQL로 옮겨 쓰면 됩니다.