반응형
JPA에서 JPQL 사용하는 법
JPA를 쓰다 보면 단순히 findById 같은 기본 메서드로는 해결이 안 되는 경우가 많다. 이럴 때 JPQL을 활용하면 더 유연하게 데이터를 가져올 수 있다. SQL이랑 비슷하지만, 테이블이 아니라 엔티티 기준으로 작성한다는 게 핵심.
1. 기본적인 JPQL 쿼리
@Query("SELECT u FROM UserInfo u WHERE u.clientCode = :clientCode")
UserInfo findByClientCode(@Param("clientCode") String clientCode);
- UserInfo 엔티티에서 clientCode가 특정 값인 데이터를 가져온다.
- 테이블이 아니라 엔티티 이름을 기준으로 조회해야 한다.
- :clientCode는 파라미터 바인딩.
2. 특정 필드만 조회
모든 데이터를 가져올 필요가 없을 때, 필요한 필드만 가져오도록 최적화할 수 있다.
@Query("SELECT u.description FROM UserInfo u WHERE u.clientCode = :clientCode")
String findDescriptionByClientCode(@Param("clientCode") String clientCode);
- description 필드만 조회해서 가볍게 가져옴.
- 성능상 이득이 클 수도 있다.
3. 여러 개의 필드 조회
필요한 값이 여러 개일 때, DTO나 Object[]로 받을 수 있다.
@Query("SELECT u.clientCode, u.description FROM UserInfo u WHERE u.status = :status")
List<Object[]> findClientCodeAndDescByStatus(@Param("status") String status);
이렇게 하면 List<Object[]>로 받아서 Object[0], Object[1] 이런 식으로 값을 꺼내야 한다.
조금 더 가독성을 높이려면 DTO로 변환해서 받을 수도 있다.
@Query("SELECT new com.example.dto.UserInfoDto(u.clientCode, u.description) FROM UserInfo u WHERE u.status = :status")
List<UserInfoDto> findClientInfoDtoByStatus(@Param("status") String status);
- new 패키지명.DTO클래스명(필드들...) 형태로 작성해야 한다.
- 생성자가 반드시 있어야 한다.
4. LIKE 검색
JPQL에서도 SQL의 LIKE 연산자를 사용할 수 있다.
@Query("SELECT u FROM UserInfo u WHERE u.description LIKE %:keyword%")
List<UserInfo> searchByDescription(@Param("keyword") String keyword);
- SQL의 LIKE '%keyword%'와 동일한 기능.
- 문자열 검색할 때 유용하다.
5. ORDER BY, LIMIT
@Query("SELECT u FROM UserInfo u WHERE u.status = :status ORDER BY u.createdAt DESC")
List<UserInfo> findByStatusOrderByCreatedAtDesc(@Param("status") String status);
- ORDER BY를 활용해 정렬 가능.
- JPQL은 LIMIT을 직접 지원하지 않기 때문에, 페이징 처리하려면 Pageable을 활용해야 한다.
@Query("SELECT u FROM UserInfo u WHERE u.status = :status")
List<UserInfo> findByStatus(@Param("status") String status, Pageable pageable);
PageRequest pageRequest = PageRequest.of(0, 10); // 0번째 페이지, 10개씩 조회
repository.findByStatus("ACTIVE", pageRequest);
- Pageable을 쓰면 자동으로 LIMIT이 적용된다.
6. UPDATE / DELETE (벌크 연산)
기본적으로 JPQL은 조회용이지만, UPDATE와 DELETE도 가능하다.
@Modifying
@Query("UPDATE UserInfo u SET u.status = :status WHERE u.clientCode = :clientCode")
int updateUserStatus(@Param("clientCode") String clientCode, @Param("status") String status);
@Modifying
@Query("DELETE FROM UserInfo u WHERE u.clientCode = :clientCode")
int deleteByClientCode(@Param("clientCode") String clientCode);
- @Modifying을 붙여야 한다.
- 반환값은 영향받은 행 개수(int).
- @Transactional을 같이 걸어주는 게 안정적.
@Modifying
@Transactional
@Query("DELETE FROM UserInfo u WHERE u.clientCode = :clientCode")
void deleteByClientCode(@Param("clientCode") String clientCode);
6. 네이티브 SQL 사용하기
JPQL이 아니라 그냥 순수 SQL을 실행하고 싶을 때
@Query(value = "SELECT * FROM user_info WHERE client_code = :clientCode", nativeQuery = true)
UserInfo findByClientCodeNative(@Param("clientCode") String clientCode);
- nativeQuery = true를 추가하면 SQL 그대로 사용 가능.
- 하지만 DBMS 종속적인 코드가 될 수 있으니 주의해야 한다.
정리
- 기본적으로 엔티티 기준으로 쿼리를 작성해야 한다.
- 필요한 필드만 조회하면 성능이 좋아진다.
- 페이징은 Pageable을 활용하면 편리하다.
- UPDATE, DELETE는 @Modifying 필요.
- 순수 SQL이 필요하면 네이티브 쿼리를 사용 가능.
JPQL을 잘 활용하면 불필요한 데이터 조회를 줄이고 성능을 최적화할 수 있다.
항상 꼭 필요한 데이터만 가져오는 습관을 들이는 게 중요하다.
반응형
'Back-End > Spring-Boot' 카테고리의 다른 글
| Mono 고급 정리 - zip, fromCallable, block까지 실전 활용 정리 (0) | 2025.05.20 |
|---|---|
| Spring WebFlux의 Mono (0) | 2025.05.20 |
| DTO ↔ Entity 변환에 대한 고민과 MapStruct (0) | 2025.03.16 |
| 파라미터 포맷 변환(String -> LocalDateTime) (0) | 2025.03.13 |
| Response Body 객체 기본생성자 (0) | 2024.11.25 |