Query By Example을 사용하여 SQL을 작성하지 않고 공백 무시 검색

아래와 같이 검색 키워드 입력란이 다수 존재했을 때에, 컨트롤러측에서 if문을 사용해 경우 나누기를 하려고 하면 꽤 힘들게 되고, 코드가 더러워집니다. 거기서 조사한 바, SpringJPA의 「Query By Example」이라고 하는 기능을 사용하면 SQL을 쓰지 않아도 공란의 파라미터를 무시해 검색할 수 있다는 것이었기 때문에 구현해 보았습니다. SQL을 사용해도 가능했습니다. 입니다만, 이쪽이 더 스마트할까라고 생각했으므로, 이쪽도 비망록으로서 남겨 둡니다. 실수가 있다면 지적해 주시면 도움이됩니다 orz

공식 문서는 여기입니다.

아래와 같은 Entity가 있었다고 해서, 그 요소의 어느 쪽이라도 검색할 수 있는 화면의 검색란이 있었을 때에는 과연 기합으로 if문을 사용해 하는 것은 무리군요
@Getter
@Setter
public class Asset {
  private Integer id;
  private Integer categoryId;
  private String adminName;
  private String assetName;
  private String remarks;
  private String serialId;
  private String purchaseDate;
  private String makerName;
  private String accessory;
  private String storingPlace;
}



그래서 아래와 같이, 컨트롤러와 서비스를 기술하면 Spring의 JPA가 화면상에서 공란인 채로 검색 버튼이 눌러져, 파라미터가 하늘의 검색 조건을 무시해 레코드 취해 와 줍니다
// Controller
@GetMapping("/search")
  public String search(@ModelAttribute("asset") Asset asset, Model model) {

    List<Asset> assets = assetService.search(asset);
    model.addAttribute("assets", assets);

    return "practice";
  }
//Service
public List<Asset> search(Asset asset) {
        ExampleMatcher customExampleMatcher = ExampleMatcher.matching()
                    .withMatcher("adminName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("assetName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("remarks", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("makerName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("serialId", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("purchaseDate", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("accessory", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
                    .withMatcher("storingPlace", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase());

        Asset asset = new Asset(asset); // Asseクラスにこれ用のコンストラクタを用意
        Example<Asset> example = Example.of(asset, customExampleMatcher); // 検索条件を作成する
        List<Asset> assetList = assetRepos.findAll(example); // Repositoryには何も書かなくてOK!
        return assetList;

이 중에서 특히 배운 것은
1. ExampleMatcher customExampleMatcher = ExampleMatcher.matching() 에서 또 한 파라미터와 일치하는 검사 결과를 가져온다. 이 시점에서 null의 파라미터는 무시해준다
2. .withMatcher() String 형 파라미터의 검색 조건을 커스터마이즈 할 수 있다
3. ExampleMatcher.GenericPropertyMatchers 다음에, 메소드를 계속하는 것으로 완전 일치·부분 일치/대문자 소문자 제한등을 설정할 수 있다
- caseSensitive() = 대소문자 구분하기
- ignoreCase() = 대소문자를 구분하지 않음
- contains() = 부분 일치
- exact() = 완전 일치
- endsWith() = 후방 일치
- startsWith = 정방향 일치
이런 식으로, ExampleMatcher의 인스턴스를 작성해 JPA에 준비되어 있는 findAll(Example) 메소드를 사용하면 OK. 덧붙여서, findAll(Example, Pageable) 메소드도 있으므로, 페이지네이션과 맞추는 것도 상당히 간단하게 할 수 있습니다.

좋은 웹페이지 즐겨찾기