Spring Data JPA 조회 기능 심화 활용

메서드 이름 기반 쿼리

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);

}

중복 제거 및 집계

DISTINCTGROUP 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);

}

태그: Spring Data JPA jpa JPQL Native Query Oracle NLSSORT

6월 1일 20:38에 게시됨