메서드 이름 기반 쿼리
Spring Data JPA에서는 Repository 인터페이스에 규칙에 맞는 메서드명을 선언하면 구현체 없이 자동으로 쿼리가 생성됩니다.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
Product findByProductName(String productName);
}비즈니스 로직에서의 사용:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Override
public Product getByName(String productName) {
return productRepository.findByProductName(productName);
}
}
정렬 조건이 포함된 쿼리
복합 정렬 조건을 적용하려면 Sort 객체를 파라미터로 전달합니다.
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
List<Product> findByCategoryNot(String category, Sort sort);
}서비스 계층에서 다중 정렬 규칙을 구성합니다:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Override
public List<Product> getProductsWithSorting() {
List<Order> sortOrders = new ArrayList<>();
sortOrders.add(new Order(Sort.Direction.DESC, "productName")
.nullsLast()
.ignoreCase());
sortOrders.add(new Order(Sort.Direction.ASC, "price")
.nullsLast());
Sort sort = Sort.by(sortOrders);
return productRepository.findByCategoryNot("DISCONTINUED", sort);
}
}
@Query 어노테이션을 활용한 직접 쿼리 작성
복잡한 조건이나 특수한 구문이 필요할 때 @Query를 사용합니다. nativeQuery = true 설정 시 DBMS 고유 문법을 활용할 수 있습니다.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberRepository extends JpaRepository<Member, Long>, JpaSpecificationExecutor<Member> {
@Query(value = "SELECT * FROM t_member WHERE member_id = :memberId AND nickname LIKE CONCAT('%', :keyword, '%')",
nativeQuery = true)
List<Member> searchMembers(@Param("memberId") Long memberId, @Param("keyword") String keyword);
}
Oracle 한글 발음순 정렬
Oracle의 NLSSORT 함수를 활용한 지역화 정렬 예시입니다.
public interface MemberRepository extends JpaRepository<Member, Long>, JpaSpecificationExecutor<Member> {
@Query(value = "SELECT * FROM TB_MEMBER WHERE dept_code = :deptCode ORDER BY NLSSORT(member_name, 'NLS_SORT=SCHINESE_PINYIN_M')",
nativeQuery = true)
List<Member> findAllOrderedByPinyin(@Param("deptCode") String deptCode);
}
부분 필드 조회
특정 컬럼만 조회하여 DTO나 인터페이스 기반 프로젝션으로 매핑할 수 있습니다.
@Repository
public interface MemberSummaryRepository extends JpaRepository<Member, Long>, JpaSpecificationExecutor<Member> {
@Query(value = "SELECT member_id, email FROM TB_MEMBER WHERE dept_code = :deptCode", nativeQuery = true)
List<MemberSummary> findSummariesByDept(@Param("deptCode") String deptCode);
}
중복 제거 및 집계
DISTINCT나 GROUP BY가 필요한 경우에도 네이티브 쿼리를 활용합니다.
@Repository
public interface OrderRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {
@Query(value = "SELECT DISTINCT status FROM t_order WHERE created_at >= :startDate", nativeQuery = true)
List<String> findUniqueStatusesSince(@Param("startDate") LocalDateTime startDate);
@Query(value = "SELECT product_type, COUNT(*) as cnt FROM t_order GROUP BY product_type HAVING COUNT(*) > :minCount",
nativeQuery = true)
List<Object[]> groupByProductTypeHaving(@Param("minCount") int minimumCount);
}