Programming/JPA & Spring Data JPA 기초

24. Specification을 이용한 검색 조건 지정

JKROH 2023. 4. 18. 15:19
반응형
해당 강의는 코드 위주로 진행됩니다. 실질적인 객체 처리를 담당하는 코드는 링크에서 확인하시길 바랍니다.

* Specification

  • 검색 조건을 생성하는 인터페이스다.
    • Criteria를 이용해서 검색 조건을 생성한다.
public interface Specification<T> extends Serializable {
    @Nullable
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder);
}
  • 리포지토리 Specificatoin을 이용해서 검색 조건을 지정한다.
    • List<T> findAll(Specification<T> spec)
public interface UserRepository extends Repository<User, String> {
    List<User> findAll(Specification<User> spec);
}
  • findAll은 spec을 이용해서 검색 조건을 생성하고 해당 검색 조건에 해당하는 엔티티를 찾아서 List로 반환한다.

 

* Specfication 구현 / 사용

  • UserNameSpecification 클래스에서 확인할 수 있다.
  • toPredicate을 오버라이딩한다.
  • UserNameSpecification 객체를 만들고 이를 findAll 에 인자로 넘겨준다.
UserNameSpecification spec = new UserNameSpecification("이름");
List<User> users = userRepoistory.findAll(spec);

 

* 람다로 간결하게 구현

  • Specification을 구현한 클래스를 매번 만들면 귀찮으니 람다식을 이용해서 스펙 객체를 생성하도록 구현해보자.
  • Spec을 생성하는 클래스를 만들고 메서드 안에 람다식을 통해 Spec객체를 구현하도록 한다.
public class UserSpecs {
    public static Specification<User> nameLie(String value {
        return (root, query, cb) -> cb.like(root.get("name"), "%" + value + "%");
}

=================

UserNameSpecification spec = UserSpecs.nameLike("이름");
List<User> users = userRepository.findAll(spec);

 

* 검색 조건 조합

  • Specification의 or / and 메서드로 이용해서 조건을 조합해서 사용할 수 있다.
    • or / and는 default 구현 되어있다.
        Specification<User> nameSpec = UserSpecs.nameLike("이름1");
        Specification<User> afterSpec = UserSpecs.createdAfter(LocalDateTime.now().minusHours(1));
        Specification<User> compositespec = nameSpec.and(afterSpec);
        List<User> users2 = userRepository.findAll(compositespec);
        
        ==================
        
        Specification<User> spec3 = UserSpecs.nameLike("이름2")
                .and(UserSpecs.createdAfter(LocalDateTime.now().minusHours(1)));
        List<User> users3 = userRepository.findAll(spec3);
  • 선택적으로 조합하는 경우
Specification<User> spec = Specification.where(null);

if (keyword != null && !keyword.trim().isEmpty())) { // keyword가 null이 아니고 빈 문자열이 아니면
    spec = spec.and(UserSpecs.nameLike(keyword)); // spec을 조합
}
if (dateTime != null) { // dateTime이 null이 아니면
    spec = spec.and(UserSpecs.createdAfter(dateTime)); // spec을 조합
}

List<User users = userRepository.findAll(spec);

 

* Specification + 페이징, 정렬

  • List<User>findAll(Specification<T> spec, Sort s)
  • Page<User>findAll(Specification<T> spec, Pageable p)
  • List<User>findAll(Specification<T> spec, Pageable p)

 

* SpecBuilder

  • 위의 검색 조건 조합에서 if 절을 덜 쓰기 위해서 SpecBuilder를 구현하면 코드를 좀 더 깔끔하게 만들 수 있다.
  • 자세한 설명은 링크에서 확인할 수 있다.

 

* 정리

  • Specification 인터페이스를 이용해서 검색 조건을 생성할 수 있다.
  • 조건의 조합도 가능하다.
    • and / or 
반응형