Specification 검색기능

hidden_·2022년 8월 1일
0

~~이건 진짜 까먹을 것 같아서 ,, 적는다. ~~

jpa 공부하는데 where 조건 넣는 방법 찾다가, Specification 이용하면 된다고 해서
Specification 이용해서 검색어 찾는 기능 만듬

설명에 불필요한 칼럼은 생략하고 엔티티 관계를 보여주면,

이런 관계가 설정됨. 상품 코드 매핑은 상품 공통코드에서 코드로 관리하고 있는 데이터들을 포함하고 있음.

Issue 1. findAll( ) 사용시, 객체 참조 무한반복

findAll() 을 사용하여 가이드 광고 상품을 조회하려면,
GoodsCodes에서 goodsId를 가진 리스트들을 가져오고, 해당 리스트의
codeId를 참조하여 CommonCode에서 codeId를 참조하여 codeNm을 가져와야함.

goodsId -> codeId -> codeNm

근데 GoodsCodes가 양방향으로부터 매핑 관계를 갖기 때문에, 무한 객체 참조 오류가 발생 ㅜ

해당 문제를 해결하기 위해서
@JsonIgnore이랑, 객체 주소 참조해서 1번만 조회하도록 하는 여러 어노테이션을 찾아서 해봤는데, 난 안됨.

그래서 dto 사용해서 나름 간단하게(?) 해결

AdGoodsDto.java

@Getter
@Setter
public class GuideAdGoodsDto {
  public short goodsId;
  private String goodsNm;
  private List<GoodsCodesDto> list = new ArrayList<GoodsCodesDto>();
  private String etc;

  public GuideAdGoodsDto(GuideAdGoods data) {
    this.goodsId = data.getGoodsId();
    this.goodsNm = data.getGoodsNm();
    for (GoodsCodes code : data.getList()) {
      list.add(new GoodsCodesDto(code));
    }
    this.etc = data.getEtc();
  }
}

GoodsCodesDto.java

@Getter
@Setter
public class GoodsCodesDto {
  private String codeNm;
  private short codeId;

  public GoodsCodesDto(GoodsCodes data) {
    this.codeName = data.getCodes().getCode().getCodeNm();
    this.code = data.getCodes().getCode().getCodeId();

  }
}

GoodsCodesDto의 경우 @EmbededId 로 구성되어 있는데, 한번 더 참조하는 dto 만들기 싫어서.. 그냥 바로 CommonCodes 내용 풀어씀 ㅎㅎ

Issue 2. Specification ?

jpa 에서 where 조건 커스텀 할라고 Specifiacation에 대해 알게됨.
나의 경우, AdGoods의 칼럼들의 내용을 검색해야함.

Specifacation을 이용하려면 클래스를 만들어줘야함. 무족권은 아니겠지 ? 항상 무조건이란건 없는듯. 하기 나름

암튼 이게 사용하는데 보니까 spec 조건을 생성하고
findAll(spec) 이런 식으로 넣어주는 거더만. 그래야 where 조건의 조건문을 생성함

자세한건 공식 문서를 참조하길..

나의 경우, etc 와 adgoodsNm의 내용을 검색하고 싶다고 했을때, 이런식으로 작성해서
Controller 에서는 searchAll을 사용하도록 했음.

@Getter
public class GuideAdGoodsSpecification {
  private String key;

  public GuideAdGoodsSpecification() {

  }

  public GuideAdGoodsSpecification(String searchKey) {
    key = "%" + searchKey + "%";
  }

  public Specification<GuideAdGoods> filterGoodsNm() {
    return (root, query, criteriaBuilder) -> (criteriaBuilder.like(root.get("goodsNm"), key));
  }

  public Specification<GuideAdGoods> filterEtc() {
    return (root, query, criteriaBuilder) -> (criteriaBuilder.like(root.get("etc"), key));
  }

  public Specification<GuideAdGoods> searchAll() {
    return this.filterGoodsNm()
        .or(this.filterEtc());
  }

}

이렇게 하면 Controller가 어떻게 되냐..
검색 기능이 가능한 findAll을 만들어 주고 싶었음.
dto를 이용하여 findAll한 검색 결과를 담아주는 과정이 포함되어야 무한 참조를 막을 수 있고,
specification을 이용하여 검색 조건을 추가함 😊

  // 상품명, etc 으로 검색
  @Operation(summary = "가이드 상품 목록", description = "가이드 상품 목록")
  @GetMapping("/goods")
  public Object getGoods(
      @RequestParam(required = false) String searchKey) {
    Specification<GuideAdGoods> spec = (root, query, criteriaBuilder) -> null;
    if (searchKey != null) {
      GuideAdGoodsSpecification filter = new GuideAdGoodsSpecification(searchKey);
      spec = spec.or(filter.searchAll());
    }
    List<GuideAdGoods> codes = new ArrayList<>();
    codes = repo.findAll(spec);
    List<GuideAdGoodsDto> list = new ArrayList<>();
    for (GuideAdGoods item : codes) {
      GuideAdGoodsDto dto = new GuideAdGoodsDto(item);
      list.add(dto);
    }
    return new ResponseEntity<Object>(list, HttpStatus.OK);
  }
profile
steady

0개의 댓글