복붙노트

[SPRING] 스프링 데이터 - 다중 열 검색

SPRING

스프링 데이터 - 다중 열 검색

나는 페이징과 정렬을 위해 Spring Data를 사용하고있다. 그러나 다중 열 검색을 수행하고 싶습니다.

자, 이제 저장소 인터페이스에서 @Query라는 어노테이션을 사용하고있다.

public interface MyRepository extends PagingAndSortingRepository<Item,Long> {

    @Query(value="select mt from MY_TABLE mt where mt.field1 = %searchtext% or mt.field2 = %searchtext% or mt.field3 = %searchtext%")    
    Page<Item> findByAllColumns(@Param("searchtext") String searchtext, Pageable pageable);

}

편집 :이 솔루션의 문제는 @Query 어노테이션의 where 절에 있습니다. 왜냐하면 우리가 검색하고자하는 모든 열에 대해 동일한 검색 텍스트 매개 변수를 반복해야하기 때문입니다 (Brandon Oakley의 코멘트 이후에 질문의 설명이 나옵니다)

테이블의 열 수가 높을 수 있기 때문에 할 수있는 다른 방법이 있는지 알고 싶습니다.

당신의 도움을 주셔서 감사합니다.

해결법

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

    1.사양을 사용할 수 있습니다. 또한 유연성이 향상됩니다. 한 가지 방법을 사용할 수 있지만 쿼리에 대해 여러 가지 사양을 사용할 수 있습니다.

    사양을 사용할 수 있습니다. 또한 유연성이 향상됩니다. 한 가지 방법을 사용할 수 있지만 쿼리에 대해 여러 가지 사양을 사용할 수 있습니다.

    Page<Item> findAll(Specification<T> spec, Pageable pageable);
    
    myRepository.findAll(textInAllColumns(searchText), pageable);
    
  2. ==============================

    2.다음은 사용자 사양과 같은 샘플입니다.

    다음은 사용자 사양과 같은 샘플입니다.

    public static Specification<User> containsTextInName(String text) {
        if (!text.contains("%")) {
            text = "%" + text + "%";
        }
        String finalText = text;
        return (root, query, builder) -> builder.or(
                builder.like(root.get("lastname"), finalText),
                builder.like(root.get("firstname"), finalText)
        );
    }
    

    또는 사용자 정의가 가능한 구현 :

    public static Specification<User> containsTextInAttributes(String text, List<String> attributes) {
        if (!text.contains("%")) {
            text = "%" + text + "%";
        }
        String finalText = text;
        return (root, query, builder) -> builder.or(root.getModel().getDeclaredSingularAttributes().stream()
                .filter(a -> attributes.contains(a.getName()))
                .map(a -> builder.like(root.get(a.getName()), finalText))
                .toArray(Predicate[]::new)
        );
    }
    
    public static Specification<User> containsTextInName(String text) {
        return containsTextInAttributes(text, Arrays.asList("lastname", "firstname"));
    }
    

    용법:

    userRepository.findAll(Specifications.where(UserSpecifications.containsTextInName("irs")))
    
  3. ==============================

    3.이전 두 가지 답변을 결합하십시오 : API와 데이터베이스 스키마를 결합하고 싶지 않거나 사용자가 문자열 열 이름을 제공하지 않게하려면 문자열이 아닌 속성을 필터링하여 적용 할 수 있습니다 있는 모든 사람들에게. 다음 예제에서는 name, field1, field2 및 field3과 같은 열 값으로 텍스트 검색을 시도합니다.

    이전 두 가지 답변을 결합하십시오 : API와 데이터베이스 스키마를 결합하고 싶지 않거나 사용자가 문자열 열 이름을 제공하지 않게하려면 문자열이 아닌 속성을 필터링하여 적용 할 수 있습니다 있는 모든 사람들에게. 다음 예제에서는 name, field1, field2 및 field3과 같은 열 값으로 텍스트 검색을 시도합니다.

    엔티티 예 :

    @Entity
    public class MyEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public int id;
        public String name;
        public String field2;
        public String field3;
        public String field4;
    }
    

    사양 예 :

    public class EntitySpecification {
    
        public static Specification<MyEntity> textInAllColumns(String text) {
    
            if (!text.contains("%")) {
                text = "%"+text+"%";
            }
            final String finalText = text;
    
            return new Specification<MyEntity>() {
                @Override
                public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> cq, CriteriaBuilder builder) {
                    return builder.or(root.getModel().getDeclaredSingularAttributes().stream().filter(a-> {
                        if (a.getJavaType().getSimpleName().equalsIgnoreCase("string")) {
                            return true;
                        }
                        else {
                            return false;
                    }}).map(a -> builder.like(root.get(a.getName()), finalText)
                        ).toArray(Predicate[]::new)
                    );
                }
            };
        }
    
     }
    

    저장소 예 :

    public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, Integer> {
        List<MyEntity> findAll(Specification<MyEntity> spec);
    }
    

    사용 예 :

    List<MyEntity> res = failureRepository.findAll(Specifications.where(FailureSpecification.textInAllColumns(text)));
    

    다른 업데이트 (lambdas가있는 필드의 흰색 목록이있는 모든 유형의 열에서 검색 - 코드가 확인되지 않음)

    public class EmployeeSpecification {
        public static Specification<Employee> textInAllColumns(String text, Set<String> fields) {
            if (!text.contains("%")) {
                text = "%" + text + "%";
            }
            final String finalText = text;
    
            return  (Specification<Employee>) (root, query, builder) -> 
                    builder.or(root.getModel().getDeclaredSingularAttributes().stream().filter(a -> {
                    return fields.contains(a.getName());
                }).map(a -> builder.like(root.get(a.getName()), finalText)).toArray(Predicate[]::new));
        }
    } 
    
  4. from https://stackoverflow.com/questions/25872637/spring-data-multi-column-searches by cc-by-sa and MIT license