https://sundaland.tistory.com/416
[ ▶ Using @Query ]
@Query 어노테이션은 Named Query를 사용하는 전통적인 방식과는 다른 유연한 쿼리 정의 방법을 제공한다. 이를 통해 쿼리를 리포지토리 인터페이스에 직접 정의할 수 있다.
[ ▷ @Query 어노테이션의 기본 개념 ]
- Named Query와의 차이: Named Query는 도메인 클래스에 선언하여 사용할 수 있는 쿼리다. 반면, @Query 어노테이션을 사용하면 쿼리를 직접 리포지토리 인터페이스의 메서드에 정의할 수 있다. 이렇게 하면 도메인 클래스가 영속성 관련 정보로부터 자유로워지고, 쿼리와 관련된 코드를 리포지토리와 함께 묶어둘 수 있다.
- 우선 순위: @Query 어노테이션이 선언된 메서드는 Named Query나 orm.xml에 정의된 Named Query보다 우선 순위가 높다. 즉, 동일한 메서드 이름으로 Named Query가 존재하더라도, @Query 어노테이션이 붙은 메서드가 호출될 때는 @Query에서 정의한 쿼리가 사용된다.
[ ▷ @Query 사용 예시 ]
▼ @Query 어노테이션을 사용하여 쿼리를 정의하는 예시
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}
위 코드에서 findByEmailAddress 메서드는 User 엔티티의 emailAddress 속성과 일치하는 사용자를 조회하는 JPQL 쿼리를 실행한다. 여기서 ?1은 메서드의 첫 번째 파라미터를 나타낸다.
[ ▷ Applying a QueryRewriter ]
▼ QueryRewriter를 사용하여 쿼리를 재작성하는 방법
public interface MyRepository extends JpaRepository<User, Long> {
@Query(value = "select original_user_alias.* from SD_USER original_user_alias",
nativeQuery = true,
queryRewriter = MyQueryRewriter.class)
List<User> findByNativeQuery(String param);
@Query(value = "select original_user_alias from User original_user_alias",
queryRewriter = MyQueryRewriter.class)
List<User> findByNonNativeQuery(String param);
}
위 코드에서 findByNativeQuery 메서드는 네이티브 SQL 쿼리를 사용하여 데이터를 조회한다. 이때 MyQueryRewriter 클래스를 통해 쿼리가 전송되기 전에 쿼리가 재작성된다.
[ ▷ QueryRewriter 구현 예시 ]
▼ 쿼리 재작성기를 구현하는 예시
public class MyQueryRewriter implements QueryRewriter {
@Override
public String rewrite(String query, Sort sort) {
return query.replaceAll("original_user_alias", "rewritten_user_alias");
}
}
위 코드에서 MyQueryRewriter 클래스는 QueryRewriter 인터페이스를 구현하여 rewrite 메서드를 정의한다. 이 메서드는 original_user_alias를 rewritten_user_alias로 교체하는 역할을 한다.
[ ▷ 리포지토리에서 QueryRewriter 제공 ]
리포지토리 자체가 QueryRewriter 인터페이스를 구현할 수도 있다.
public interface MyRepository extends JpaRepository<User, Long>, QueryRewriter {
@Query(value = "select original_user_alias.* from SD_USER original_user_alias",
nativeQuery = true,
queryRewriter = MyRepository.class)
List<User> findByNativeQuery(String param);
@Query(value = "select original_user_alias from User original_user_alias",
queryRewriter = MyRepository.class)
List<User> findByNonNativeQuery(String param);
@Override
default String rewrite(String query, Sort sort) {
return query.replaceAll("original_user_alias", "rewritten_user_alias");
}
}
위 코드에서 MyRepository 인터페이스는 QueryRewriter를 구현하여 rewrite 메서드를 오버라이드한다. 이 방법을 사용하면 QueryRewriter를 별도로 작성하지 않고도 쿼리를 재작성할 수 있다.
[ ▷ Using Advanced LIKE Expressions ]
Spring Data JPA에서 @Query를 사용하여 쿼리를 수동으로 정의할 때, 고급 LIKE 표현식을 정의할 수 있는 기능이 있다. 이 기능은 LIKE 쿼리에서 패턴을 사용하여 문자열 검색을 더 유연하게 만들어준다.
[ ▷ 고급 LIKE 표현식 정의 ]
예를 들어, 사용자의 이름이 특정 패턴으로 끝나는지를 확인하는 쿼리를 정의할 수 있다. 다음은 @Query를 사용하여 LIKE 표현식을 활용한 예시다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname like %?1")
List<User> findByFirstnameEndsWith(String firstname);
}
[ ▷ 쿼리 설명 ]
- 쿼리 구조: 위의 예에서 @Query 어노테이션으로 정의된 쿼리는 사용자의 이름(firstname)이 특정 문자열로 끝나는지를 확인하는 쿼리다.
- LIKE 패턴: % 기호는 SQL에서 와일드카드(wildcard)로 사용되어, 어떤 문자열이든 올 수 있음을 나타낸다. 여기서 ?1은 메서드의 첫 번째 파라미터를 의미한다.
- 변경된 쿼리: 이 쿼리는 JPQL 쿼리로 변환되며, % 문자가 자동으로 인식되고 쿼리 실행 시 파라미터에 추가된다. 즉, 사용자가 메서드를 호출할 때 제공한 문자열이 이 % 기호와 결합되어 실제 쿼리에서 사용된다.
[ ▷ 사용 예시 ]
만약 findByFirstnameEndsWith 메서드를 호출하여 "son"이라는 문자열을 전달한다고 가정다. 이 경우 최종적으로 실행되는 쿼리는 다음과 같이 변환된다.
SELECT u FROM User u WHERE u.firstname LIKE '%son'
이 쿼리는 사용자의 이름이 "son"으로 끝나는 모든 User 엔티티를 검색한다.
[ ▷ 결과 ]
- 유연한 검색: 이렇게 고급 LIKE 표현식을 사용하면 사용자가 원하는 패턴에 맞는 결과를 더 유연하게 검색할 수 있다. 단순한 문자열 일치를 넘어서서, 부분 문자열 검색이 가능해진다.
- 편리한 메서드 호출: 사용자는 간단하게 메서드를 호출하여 복잡한 쿼리를 수동으로 작성할 필요 없이 원하는 결과를 얻을 수 있다.
Spring Data JPA의 @Query 어노테이션을 사용하면 LIKE 표현식을 쉽게 정의하고 활용할 수 있다. 이 기능은 특히 동적 검색이 필요한 경우에 매우 유용하며, 코드의 가독성과 유지보수성을 높여주는 효과가 있다. 이를 통해 데이터베이스와의 상호작용을 더욱 간결하고 직관적으로 만들 수 있다.
[ ▷ Native Queries ]
Spring Data JPA에서 @Query 어노테이션을 사용하면 네이티브 쿼리를 실행할 수 있다. 네이티브 쿼리는 데이터베이스에 직접적인 SQL 쿼리를 작성하여 실행하는 방법이다.
[ ▷ 네이비트 쿼리 정의 ]
네이티브 쿼리를 정의하려면 @Query 어노테이션의 nativeQuery 플래그를 true로 설정해야 다. 다음은 이메일 주소로 사용자를 찾는 네이티브 쿼리의 예시다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
위의 쿼리는 데이터베이스의 USERS 테이블에서 EMAIL_ADDRESS가 주어진 인자와 일치하는 사용자를 검색한다.
[ ▷ 네이티브 쿼리에서 동적 정렬 지원 ]
현재 Spring Data JPA는 네이티브 쿼리에서 동적 정렬을 지원하지 않는다. 이는 실제 SQL 쿼리를 조작해야 하는데, 이는 네이티브 SQL에서는 신뢰할 수 없기 때문이다. 하지만, 페이지네이션은 가능합니다. 다음은 페이지네이션을 위해 카운트 쿼리를 직접 지정한 예시다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
이 쿼리는 주어진 LASTNAME으로 사용자를 검색하는 네이티브 쿼리와 해당 사용자의 수를 세기 위한 카운트 쿼리를 동시에 정의한다. 이처럼 네이티브 쿼리와 카운트 쿼리를 함께 사용하여 페이지네이션 기능을 구현할 수 있다.
[ ▷ 네이티브 쿼리 파서 설정 ]
Spring Data JPA에서는 네이티브 쿼리 파서로 JSqlParser를 사용할 수 있지만, 이를 비활성화할 수도 있다. spring.data.jpa.query.native.parser를 regex로 설정하면 regex 기반 쿼리 향상 도구를 사용할 수 있다. 가능 값은 다음과 같다.
- auto: 기본값으로, 자동 선택.
- regex: 내장된 regex 기반 쿼리 향상기 사용.
- jsqlparser: JSqlParser 사용.
[ ▷ 네이티브 결과 읽기 ]
네이티브 쿼리를 사용하면 데이터베이스의 실제 컬럼 이름과 값을 키/값 쌍으로 나타내는 네이티브 튜플을 읽을 수 있다. 아래는 네이비트 데이터를 리턴하는 예제이다.
interface UserRepository extends JpaRepository<User, Long> {
@NativeQuery("SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1")
Map<String, Object> findRawMapByEmail(String emailAddress); // (1)
@NativeQuery("SELECT * FROM USERS WHERE LASTNAME = ?1")
List<Map<String, Object>> findRawMapByLastname(String lastname); // (2)
}
- (1): 단일 Map 결과를 반환하며, 각 키는 데이터베이스의 컬럼 이름, 값은 해당 컬럼의 값을 나타다.
- (2): 여러 개의 Map 결과를 반환하며, 각 Map은 각 사용자에 대한 정보를 포함한다.
[ ▷ 주요 사항 ]
- Tuple Queries: 문자열 기반의 튜플 쿼리는 Hibernate에서만 지원하며, EclipseLink는 Criteria 기반의 튜플 쿼리만 지원한다.
Spring Data JPA의 네이티브 쿼리는 데이터베이스와의 직접적인 상호작용을 가능하게 하며, 복잡한 SQL 쿼리를 자유롭게 작성할 수 있는 장점을 제공한다. 그러나 네이티브 쿼리를 사용할 때는 SQL 문법에 대한 이해가 필요하며, Spring Data JPA의 다른 기능들에 비해 동적 쿼리 생성 기능이 제한적이라는 점을 고려해야 한다.
'Spring Boot > Spring Data JPA' 카테고리의 다른 글
Query Hint (0) | 2024.10.24 |
---|---|
Scrolling (0) | 2024.10.24 |
Other Methods, Modifying Queries (0) | 2024.10.24 |
Using Sort, Scrolling Large Query Results (0) | 2024.10.24 |
Using JPA Named Queries (0) | 2024.10.24 |