반응형
스프링 데이터 JPA 기능 중에서 Pageable과 Page를 사용하면 쉽게 페이징 처리를 할 수 있어 편리하다. 하지만 특정 행부터 일정 개수의 데이터를 조회하고 싶은 경우에는 Pageable과 Page가 적합하지 않다(예를 들어 21번째 행부터 21개의 데이터를 읽어오고 싶은 경우). 특정 행부터 일정 개수의 데이터를 조회할 수 있는 기능을 모든 리포지토리에 적용할 필요가 생겼는데 이를 위해 다음 작업을 진행했다.
- Rangeable 클래스 추가 : 조회할 범위 값 저장(Pageable 대체).
- RangeableExecutor 인터페이스 : Rangeable 타입을 사용하는 조회 메서드 정의.
- RangeableRepository 인터페이스 : 스프링 데이터 JPA Repository 인터페이스와 RangeableExecutor 인터페이스를 상속.
- RangeableRepositoryImpl 클래스 : 스프링 데이터 JPA의 기본 구현체를 확장. RangeableRepository 인터페이스의 구현을 제공.
스프링 데이터 JPA에서 모든 리포지토리에 동일 기능을 추가하는 방법은 스프링 데이터 JPA 레퍼런스를 참고한다.
예제 코드 : https://github.com/madvirus/spring-data-jpa-rangeable
Rangeable 클래스
import org.springframework.data.domain.Sort;
public class Rangeable {
private int start;
private int limit;
private Sort sort;
public Rangeable(int start, int limit, Sort sort) {
this.start = start;
this.limit = limit;
this.sort = sort;
}
public int getStart() {
return start;
}
public int getLimit() {
return limit;
}
public Sort getSort() {
return sort;
}
}
* start : 시작행, limit : 개수, sort : 정렬
RangeableExecutor 인터페이스
import org.springframework.data.jpa.domain.Specification;
import java.util.List;
public interface RangeableExecutor<T> {
List<T> getRange(Specification<T> spec, Rangeable rangeable);
}
RangeableRepository 인터페이스
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import java.io.Serializable;
@NoRepositoryBean
public interface RangeableRepository<T, ID extends Serializable>
extends Repository<T, ID>, RangeableExecutor<T> {
}
RangeableRepositoryImpl 클래스
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.io.Serializable;
import java.util.List;
public class RangeableRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements RangeableRepository<T, ID> {
public RangeableRepositoryImpl(
JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
}
@Override
public List<T> getRange(Specification<T> spec, Rangeable rangeable) {
TypedQuery<T> query = getQuery(
spec, getDomainClass(), rangeable.getSort());
query.setFirstResult(rangeable.getStart());
query.setMaxResults(rangeable.getLimit());
return query.getResultList();
}
}
* 기본 구현체인 SimpleJpaRepository 클래스를 확장해서 getRange() 구현
@EnableJpaRepositories로 기본 구현 지정
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@EnableJpaRepositories(repositoryBaseClass = RangeableRepositoryImpl.class)
public class SpringJpaConfiguration {
}
리포지토리에서 RangeableExecutor 인터페이스 사용
import org.springframework.data.repository.Repository;
import rangeable.jpa.RangeableExecutor;
public interface CommentRepository
extends Repository<Comment, Long>, RangeableExecutor<Comment> {
}
Rangeable로 일정 범위 조회
List<Comment> comments = repository.getRange(
someSpec,
new Rangeable(10, 5, Sort.by("id").descending()));