복붙노트

[SPRING] JPA 및 Spring 부트에 대한 검색 기능 구현 [닫기]

SPRING

JPA 및 Spring 부트에 대한 검색 기능 구현 [닫기]

데이터베이스 테이블에 대한 검색 기능을 올바르게 구현하려고합니다. 나는이 접근법을 시도했다 :

제어 장치:

    @GetMapping
    public Page<TransactionDTO> find(TransactionFilterDTO filter, Pageable page) {
        return searchRepository
                .findTransactionsByFilter(mapper.toFilter(filter), page)
                .map(mapper::toDTO);
    }

파일 DTO :

public class TransactionFilterDTO {

    private String name;

    private Integer id;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime from;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime to;
    ... // getters and setter
}

검색 구현 :

@Repository
public class TransactionSearchRepositoryImpl implements TransactionSearchRepository {

    @Autowired
    private TransactionRepository transactionRepository;

    @Autowired
    private TransactionSpecification specification;

    @Override
    public Page<Transaction> findTransactionsByFilter(TransactionFilter filter, @Nullable Pageable page) {


        List<Transaction> transactions = transactionRepository
                .findAll(specification.getFilter(filter));

        int totalCount = transactions.size();

        if(page != null) {
           transactions = transactions
                   .stream()
                   .skip(page.getOffset())
                   .limit(page.getPageSize())
                   .collect(Collectors.toList());
        }

        return new PageImpl<>(transactions, page, totalCount);
    }
}

저장소:

public interface TransactionSearchRepository {

    Page<Transaction> findTransactionsByFilter(TransactionFilter filter, Pageable page);
}

검색 기능을 구현하는 더 좋은 방법이 있습니까? 이 솔루션은 내 견해로는 매우 추악합니다.

해결법

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

    1.이러한 작업을 해결하기 위해 여러 가지 방법을 권장 할 수 있습니다.

    이러한 작업을 해결하기 위해 여러 가지 방법을 권장 할 수 있습니다.

    1) 단순하지만 융통성 없음 : 필터 속성 (이름, 시작, 끝)에 따라 컨트롤러에서 요청 매개 변수를 사용하고 리포지토리에서 해당 쿼리를 준비하십시오.

    제어 장치:

    @GetMapping("/q")
    public List<ResponseDto> getAllByQuery(
        @RequestParam(value = "name", required = false) String name,
        @RequestParam(value = "from", required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate from,
        @RequestParam(value = "to", required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate to,
        Pageable pageable
    ) {
        return service.getByQuery(name, from, to, pageable);
    }
    

    서비스:

    public Page<ResponseDto> getByQuery(String name, LocalDate from, LocalDate to, Pageable pageable) {
        return repo.getByQuery(name, from, to, pageable).map(mapper::toResponseDto);
    }
    

    저장소:

    @Query("select m from MyEntity m where " +
           "(?1 is null or upper(m.name) like concat('%', upper(?1), '%')) " +
           "and (?2 is null or m.createdAt >= ?2) " +
           "and (?3 is null or m.createdAt <= ?3)")
    Page<MyEntity> getByQuery(String name, final LocalDate from, final LocalDate to, final Pageable pageable);
    

    그런 다음 요청을 수행하십시오.

    GET http://localhost:8080/q?name=john&from=2019-04-19&to=2019-04-19
    

    2) QueryDsl 사용. 프로젝트에 추가하고 (여기에서 세부 사항을 찾을 수 있음) QuerydslPredicateExecutor 및 QuerydslBinderCustomizer에서 리포지토리를 확장하고 '튜닝'을 추가하십시오.

    public interface MyEntityRepo extends JpaRepository<MyEntity, Integer>, QuerydslPredicateExecutor<MyEntity>, QuerydslBinderCustomizer<QMyEntity> {
        @Override
        default void customize(@NonNull QuerydslBindings bindings, @NonNull QMyEntity entity) {
    
            // Make case-insensitive 'like' filter for all string properties
            bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
    
            // Add 'between' and 'greater or equal' filter date property
            bindings.bind(entity.createdAt).all((path, value) -> {
                Iterator<? extends LocalDate> it = value.iterator();
                LocalDate from = it.next();
                if (value.size() >= 2) {
                    LocalDate to = it.next();
                    return Optional.of(path.between(from, to)); // between
                } else {
                    return Optional.of(path.goe(from)); // greater than or equal
                }
            });
        }
    

    서비스 방법 추가 :

    public Page<ResponseDto> getAllByQueryDsl(Predicate predicate, Pageable pageable) {
        return repo.findAll(predicate, pageable).map(mapper::toResponseDto);
    }
    

    컨트롤러 방법 추가 :

    @GetMapping("/query-dsl")
    public Page<ResponseDto> getAllByQueryDsl(
            @QuerydslPredicate(root = MyEntity.class, bindings = MyEntityRepo.class) Predicate predicate,
            Pageable pageable
    ) {
        return service.getAllByQueryDsl(predicate, pageable);
    }
    

    그리고 엔티티의 'Date'속성에 @DateTimeFormat 주석을 추가하십시오.

    @Entity
    public class MyEntity {
        // ...
        @DateTimeFormat(iso = ISO.DATE) private LocalDate createdAt;
    }
    

    그런 다음 이러한 요청을 수행 할 수 있습니다.

    GET http://localhost:8080/query-dsl?name=john&createdAt=2019-04-15&createdAt=2019-04-19
    

    첫 번째 날짜는 '시작'매개 변수이고 두 번째 날짜는 '끝'매개 변수입니다. 하나의 날짜 만 사용하는 경우 '시작'매개 변수 (보다 크거나 같음)입니다.

    3) spec-arg-resolver 라이브러리 사용. 프로젝트에 추가 한 다음 (명령 : 1 및 2 참조) JpaSpecificationExecutor에서 리포지를 확장하십시오.

    public interface MyEntityRepo extends JpaRepository<MyEntity, Integer>, JpaSpecificationExecutor<MyEntity> {}
    

    컨트롤러에 이러한 방법을 추가하십시오.

    @GetMapping("/specification")
    public Page<ResponseDto> getAllBySpecification(
            @And({
                    @Spec(path = "name", spec = LikeIgnoreCase.class),
                    @Spec(path = "createdAt", params = "from", spec = GreaterThanOrEqual.class),
                    @Spec(path = "createdAt", params = "to", spec = LessThanOrEqual.class)
            }) Specification<MyEntity> specification,
            Pageable pageable
    ) {
        return service.getAllBySpecification(specification, pageable);
    }
    

    서비스 업데이트 :

    public Page<ResponseDto> getAllBySpecification(final Specification<MyEntity> specification, final Pageable pageable) {
        return repo.findAll(specification, pageable).map(mapper::toResponseDto);
    }
    

    그런 다음 데이터를 요청하십시오.

    GET http://localhost:8080/specification?name=john&from=2019-04-10&to=2019-04-19
    

    4) 사양 매뉴얼 작성 :

    필터 클래스를 작성하십시오.

    @Data
    public class MyFilter implements Specification<MyEntity> {
        private String name;
        @DateTimeFormat(iso = ISO.DATE) private LocalDate from;
        @DateTimeFormat(iso = ISO.DATE) private LocalDate to;
    
        @Override
        public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            List<Predicate> predicates = new ArrayList<>();
            if (name != null) predicates.add(builder.like(builder.upper(root.get("name")), "%" + name.toUpperCase() + "%"));
            if (from != null) predicates.add(builder.greaterThanOrEqualTo(root.get("createdAt"), from));
            if (to != null) predicates.add(builder.lessThanOrEqualTo(root.get("createdAt"), to));
            return builder.and(predicates.toArray(new Predicate[0]));
        }
    }
    

    컨트롤러 메소드를 작성하십시오.

    @GetMapping("/filter")
    public Page<ResponseDto> getAllByMyFilter(MyFilter filter, Pageable pageable) {
        return service.getAllBySpecification(filter, pageable);
    }
    
    

    그런 다음 요청을 실행하십시오.

    GET http://localhost:8080/filter?name=john&from=2019-04-10&to=2019-04-19
    
  2. ==============================

    2.예를 들어 쿼리를 살펴보십시오. 당신은 단순히 사용하여 일부 코드를 제거 할 수 있습니다

    예를 들어 쿼리를 살펴보십시오. 당신은 단순히 사용하여 일부 코드를 제거 할 수 있습니다

    transactionRepository.findAll(Example.of(transaction));
    

    또한 페이지 매김 및 고급 API 검사 ExampleMatcher 클래스를 지원합니다.

  3. ==============================

    3.이 문제를 해결하는 방법에는 여러 가지가 있습니다. 다음 솔루션도 구현할 수 있습니다. 전의. 귀하의 저장소에서 :

    이 문제를 해결하는 방법에는 여러 가지가 있습니다. 다음 솔루션도 구현할 수 있습니다. 전의. 귀하의 저장소에서 :

    @Query(value="FROM Students AS student "
                + " LEFT JOIN FETCH student.school AS school"
                .......................
                ...................
                + " WHERE  LOWER(student.name) LIKE LOWER(CONCAT('%',:criteria, '%')) OR"
                + " LOWER(student.code) LIKE LOWER(CONCAT('%',:criteria, '%')) OR"
                .........................
                .........................
                .........................
                + " LOWER(school.name) LIKE LOWER(CONCAT('%',:criteria, '%')) "         
                + " ORDER BY student.name ASC",
                countQuery=" SELECT COUNT(student) from Students AS student ")
        public Page<Students> getAllStudentsBySearchCriteria(@Param("criteria")String search, Pageable pageable);
    

    귀하의 서비스에서 :

    public Page<Students> getAll(Page  pagable) {
        return studentRepository.getAllStudentsBySearchCriteria(searchString, pageable);
    
    }
    
  4. from https://stackoverflow.com/questions/55707052/implement-search-functionality-for-jpa-and-spring-boot by cc-by-sa and MIT license