복붙노트

[SPRING] 조건 검색 필터를 구현

SPRING

조건 검색 필터를 구현

나는 여러 하위 조건 검색 기능을 구현하고 싶습니다. 나는이 시도 :

    @GetMapping("find")
    public Page<PaymentTransactionsDTO> getAllBySpecification(
            @And({
                    @Spec(path = "name", spec = LikeIgnoreCase.class),
                    @Spec(path = "unique_id", spec = LikeIgnoreCase.class),
                    @Spec(path = "createdAt", params = "from", spec = GreaterThanOrEqual.class),
                    @Spec(path = "createdAt", params = "to", spec = LessThanOrEqual.class)
            }) Specification<PaymentTransactions> specification,
            Pageable pageable
    ) {        
        return transactionService.getAllBySpecification(specification, pageable));       
    }

저장소:

      @Override
      public Page<PaymentTransactions> getAllBySpecification(final Specification<PaymentTransactions> specification, final Pageable pageable) {
          return dao.findAll(specification, pageable);
      }

현재이 요청하고있다 :

GET /api/transactions/find?unique_id=22&page=0&size=10 

뿐만 아니라 나는 단지 UNIQUE_ID에 대한 기본 검색을 보내 이러한 추가 검색 조건을 구현하려는 :

start with 
=
end with 
contains

https://github.com/tkaczmarzyk/specification-arg-resolver를 사용하여 추가 하위 조건을 보낼 수있는 몇 가지 방법은 무엇입니까? 나는이 값을 보낼 수있는 가장 좋은 방법이 무엇인지 일반적으로이 문제에 대한 해결책을 찾을 수없는 이유는 무엇입니까?

해결법

  1. ==============================

    1.당신은 당신의 아주 특별한 필터를 만들려면 나는 당신이 당신의 검색 인터페이스의 발명과 함께 시작해야한다 생각합니다. 그런 예를 들면 :

    당신은 당신의 아주 특별한 필터를 만들려면 나는 당신이 당신의 검색 인터페이스의 발명과 함께 시작해야한다 생각합니다. 그런 예를 들면 :

    GET /models?name=eq(john smith)&createdAt=between(2019-01-01,2019-01-31)
    GET /models?name=like(sm)&createdAt=from(2019-01-01)
    GET /models?name=sw(john)&createdAt=to(2019-01-31)
    

    그 후, 당신은 그것을 구현하려고 할 수 있습니다.

    IMO 등의 작업을 해결하는 가장 좋은 방법은 스프링 데이터 JPA 사양 (그리고 JPA 기준 API)를 사용하는 것입니다. 예를 들면 :

    1)의 우리의 개체 모델에 대한 사양을 구현하는 필터 클래스를 만들어 보자 :

    @Value
    public class ModelFilter implements Specification<Model> {
    
        private String name;
        private String createdAt;
    
        @Override
        public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    
            List<Predicate> predicates = new ArrayList<>();
    
            // Prepare predicates and fill the list with them...
    
            return builder.and(predicates.toArray(new Predicate[0]));
        }
    }
    

    2) 그 후, 제어 방법을 생성 :

    @GetMapping
    public List<Model> getAllByFilter(ModelFilter filter) {
        return repo.findAll(filter); 
    }
    

    모든 것은)) 우리의 술어를 준비 할 일은 남아

    이 작업을하려면, 우리는 편리한 '술어 빌더 인터페이스를 처음 만들 수 있습니다 :

    @FunctionalInterface
    interface PredicateBuilder<T> {
    
        Optional<Predicate> get(String fieldName, String value, Root<T> root, CriteriaBuilder builder);
    
        static Matcher getMatcher(String op, String value) {
            return getMatcher(op, value, "(.+)");
        }
    
        static Matcher getMatcher(String op, String value, String pattern) {
            return Pattern.compile(op + "\\(" + pattern + "\\)").matcher(value);
        }
    }
    

    그런 다음 우리의 조건을 만들려고 :

    같은

    PredicateBuilder<Model> eq = (fieldName, value, root, cb) -> {
        Matcher m = getMatcher("eq", value);
        if (m.matches()) {
            return Optional.of(cb.equal(cb.upper(root.get(fieldName)), m.group(1).toUpperCase()));
        } else {
            return Optional.empty();
        }
    };
    

    처럼

    PredicateBuilder<Model> like = (fn, value, root, cb) -> {
        Matcher m = getMatcher("like", value);
        if (m.matches()) {
            return Optional.of(cb.like(cb.upper(root.get(fn)), "%" + m.group(1).toUpperCase() + "%"));
        } else {
            return Optional.empty();
        }
    };
    

    시작

    PredicateBuilder<Model> sw = (fn, value, root, cb) -> {
        Matcher m = getMatcher("sw", value);
        if (m.matches()) {
            return Optional.of(cb.like(cb.upper(root.get(fn)), m.group(1).toUpperCase() + "%"));
        } else {
            return Optional.empty();
        }
    };
    

    중에서

    PredicateBuilder<Model> between = (fn, value, root, cb) -> {
        Matcher m = getMatcher("between", value, "(.+)\\s*,\\s*(.+)");
        if (m.matches()) {
            LocalDate from = LocalDate.parse(m.group(1));
            LocalDate to = LocalDate.parse(m.group(2));
            return Optional.of(cb.between(root.get(fn), from, to));
        } else {
            return Optional.empty();
        }
    };
    

    ...에서

    PredicateBuilder<Model> from = (fn, value, root, cb) -> {
        Matcher m = getMatcher("from", value);
        if (m.matches()) {
            LocalDate from = LocalDate.parse(m.group(1));
            return Optional.of(cb.greaterThanOrEqualTo(root.get(fn), from));
        } else {
            return Optional.empty();
        }
    };
    

    PredicateBuilder<Model> to = (fn, value, root, cb) -> {
        Matcher m = getMatcher("to", value);
        if (m.matches()) {
            LocalDate to = LocalDate.parse(m.group(1));
            return Optional.of(cb.lessThanOrEqualTo(root.get(fn), to));
        } else {
            return Optional.empty();
        }
    };
    

    이 필터 클래스를 완료하는 데 남아있다 :

    @Value
    public class ModelFilter implements Specification<Model> {
    
        private String name;
        private String createdAt;
    
        PredicateBuilder<Model> eq = ... ;
        PredicateBuilder<Model> like = ... ;
        PredicateBuilder<Model> sw = ... ;
        PredicateBuilder<Model> between = ... ;
        PredicateBuilder<Model> from = ... ;
        PredicateBuilder<Model> to = ... ;
    
        @Override
        public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    
            List<Predicate> predicates = new ArrayList<>();
    
            if (name != null) {
                eq.get("name", name, root, builder).ifPresent(predicates::add);
                like.get("name", name, root, builder).ifPresent(predicates::add);
                sw.get("name", name, root, builder).ifPresent(predicates::add);
            }
    
            if (createdAt != null) {
                between.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
                from.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
                to.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
            }
    
            return builder.and(predicates.toArray(new Predicate[0]));
        }
    }
    

    물론 구현의 한 예입니다. 당신은 사양 및 당신이 필요로하는 조건의 당신의 고유의 구현을 만들 수 있습니다. 여기에 주요 사항은 다음과 같습니다

  2. from https://stackoverflow.com/questions/55919913/implement-search-filter-with-conditions by cc-by-sa and MIT license