본문 바로가기

Back-End/Spring-Boot

JPA에서 JPQL 사용하는 법

반응형

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을 잘 활용하면 불필요한 데이터 조회를 줄이고 성능을 최적화할 수 있다.
항상 꼭 필요한 데이터만 가져오는 습관을 들이는 게 중요하다.

반응형