백엔드/Spring

[Spring Boot] JPA Specification 이용하여 다중 검색 조건 사용하기(string, long, enum)

작은소행성 2022. 11. 21. 23:34

JPA Specification

JPA 를 사용할 때 Repository 에서

id 를 검색할 경우 findById

Id 와 name을 검색하고자 하는 경우 findByIdAndName 으로 쿼리 메서드를 만들 수 있다. 

하지만 검색 조건이 많아질수록 쿼리 메소드가 길어지면서 가독성도 떨어지고 비효율적이다. 

해서 JPA 에서 Specification 으르 제공한다. 

Specification 을 사용해 원하는 조건을 상황에 맞게 선택해서 추가할 수 있다. 

 

 

사용법

 

MemberEntity 생성하기

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
@SuperBuilder
public class MemberEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false)
    private Long id; // id

    @Column(name = "name")
    private String name;

    @Column(name = "password")
    private String password;

    @Column(name = "email")
    private String email;
    
    @Column(name = "status")
    private StatusEnum status;
    
    @Type(type = "yes_no")
    @Column(name = "delete_yn", nullable = false, columnDefinition = "CHAR(1) DEFAULT 'N'")
    private Boolean deleteYn;
    
    @Column(nullable = false)
    private LocalDateTime createdDatetime = LocalDateTime.now();

    private LocalDateTime updatedDatetime;
    
}

 

 

MemberDto 생성하기

@Builder
@Getter
@AllArgsConstructor
public class MemberDto {

    private Long id;

    private String name;

    private String password;

    private String email;

}

 

 

StatusEnum 

 

 

 

 

MemberRepository 인터페이스 생성 후 JpaSpecificationExecutor 상속받기

public interface MemberRepository extends JpaRepository<MemberEntity, Long>, JpaSpecificationExecutor<MemberEntity> {
    Page<MemberEntity> findAll(Specification<MemberEntity> spec, Pageable pageable);
}

 

 

MemberSpecification 클래스 생성후 쿼리 조건 추가하기

public class MemberSpecification {
    public static Specification<MemberDto> equalsName(String name) {
        return (root, query, CriteriaBuilder) -> CriteriaBuilder.equal(root.get("name"),name);
    }

    public static Specification<MemberDto> equalsEmail(String email) {
        return (root, query, CriteriaBuilder) -> CriteriaBuilder.like(root.get("email"),"%" + email + "%");
    }

    public static Specification<MemberDto> betweenCreatedDatetime(LocalDateTime startDatetime, LocalDateTime endDatetime) {
        return (root, query, CriteriaBuilder) -> CriteriaBuilder.between(root.get("createdDatetime"),startDatetime, endDatetime);
    }
    
    public static Specification<ContributionHistoryDto> equalStatus(String status) {
        return (root, query, CriteriaBuilder) -> CriteriaBuilder.equal(root.get("status"), StatusEnum.valueOf(status));
    }
    
}
  • equal(root.get("name"),name);
    •  name = ?
  • like(root.get("email"), "%" + email + "%");
    • email like ?
  • between(root.get("name"),name);
    • created_datetime between ? and ?

Enum의 경우 String 으로 해당 값을 받아오나 Enum.valueOf 로 한번 감싸서 사용해야한다.

 

 

 

Controller

@Slf4j
@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    @GetMapping("/search")
    public MemberDto getCompanyList(
              @RequestParam(value = "name", required = false) String searchName
            , @RequestParam(value = "email", required = false) String searchEmail
            , @RequestParam(value = "status", required = false) String searchStatus
            , @Parameter(name = "page") Pageable pageable) {

        return memberService.getMemberSearch(searchName, searchEmail, searchStatus, pageable);
    }
    
}

 

 

Service

@Service
@RequiredArgsConstructor
public class MemberService{

    private final MemberService memberService;
    
    public MemberDto getMemberSearch(String name, String email, Pageable pageable){
        Specification<MemberDto> spec = (root, query, criteriaBuilder) -> null;

        if(name != null)
            spec = spec.and(MemberSpecification.equalName(name));
        else if(email != null)
            spec = spec.and(MemberSpecification.likeEmail(email));
        else if(email != null)
            spec = spec.and(MemberSpecification.equalStatus(email));
    
        return repository.findAll(spec,pageable);
    }

}

 

 

 

 

 

 

포스트맨 테스트

 

 

반응형