groti's blog

[Spring Boot ] JPA Specification 이용하여 쿼리 조건 다루기 본문

Spring

[Spring Boot ] JPA Specification 이용하여 쿼리 조건 다루기

groti 2020. 8. 15. 17:46

JPA를 사용할 때 쿼리 메서드를 통해 where 조건 추가할 수 있다.
예를 들어, Id로 검색할 경우 findById, Idname으로 검색하려면 findByIdAndName으로 쿼리 메서드를 만들 수 있다.

하지만 함께 검색할 검색의 조합이 다양해지면 만들어야 하는 쿼리 메서드도 많아진다.

이럴 때 Specification을 이용하면 원하는 조건을 상황에 맞게 선택하여 추가할 수 있다.

 

 

Todo DTO 클래스 만들기

@Entity
@Table(name = "TODO")
@NoArgsConstructor
@Data
public class Todo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long todoId;

    private String contents;

    @Column(nullable = false)
    private LocalDateTime createdDatetime = LocalDateTime.now();

    private LocalDateTime updatedDatetime;

    @ColumnDefault(value="'N'")
    private String completeYn;
}

 

 

 

JpaTodoRepository 인터페이스 만들고 JpaSpecificationExecutor 상속 받기

import com.leanne.edu.springboot.todo.model.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import java.util.List;

public interface JpaTodoRepository extends JpaRepository<Todo, Integer>, JpaSpecificationExecutor<Todo> {
}

List<T> findAll(@Nullable Specification<T> spec); 

Specification을 인자로 받는 findAll 함수를 사용하기 위해서는 인터페이스에 JpaSpecificationExecutor를 상속받아야 한다.

 

 

 

TodoSpecification 클래스 만들고 쿼리 조건 함수로 추가하기

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDateTime;

public class TodoSpecification {

    public static Specification<Todo> equalTodoId(Long todoId) {
        return new Specification<Todo>() {
            @Override
            public Predicate toPredicate(Root<Todo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                // 1) equal
                return criteriaBuilder.equal(root.get("todoId"), todoId);
            }
        };
    }

    public static Specification<Todo> likeContents(String contents) {
        return new Specification<Todo>() {
            @Override
            public Predicate toPredicate(Root<Todo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                // 2) like
                return criteriaBuilder.like(root.get("contents"), "%" + contents + "%");
            }
        };
    }

    public static Specification<Todo> betweenCreatedDatetime(LocalDateTime startDatetime, LocalDateTime endDatetime) {
        return new Specification<Todo>() {
            @Override
            public Predicate toPredicate(Root<Todo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                // 3) between
                return criteriaBuilder.between(root.get("createdDatetime"), startDatetime, endDatetime);
            }
        };
    }
}

1) criteriaBuilder.equal(root.get("todoId"), todoId)

-> todo_id = ?

 

2) criteriaBuilder.like(root.get("contents"), "%" + contents + "%")

-> content like = ?

 

3) criteriaBuilder.between(root.get("createdDatetime"), startDatetime, endDatetime)

-> created_datetime between ? and ?

 

 

 

검색 조건에 맞게 쿼리 조건 추가하기

public List<Todo> searchTodoList(Long todoId, String contents, LocalDateTime startDatetime, LocalDateTime endDatetime) {

    Specification<Todo> spec = Specification.where(TodoSpecification.equalTodoId(todoId));
    if(contents != null) {
    	spec = spec.and(TodoSpecification.likeContents(contents));
    }
    if(startDatetime != null && endDatetime != null) {
    	spec = spec.and(TodoSpecification.betweenCreatedDatetime(startDatetime, endDatetime));
    }

    return jpaTodoRepository.findAll(spec);
}

함수 인자를 확인하여 포함된 검색 조건은 spec 추가하고 그렇지 않은 조건은 spec에서 제외한다.

 

 

 

실행 결과 확인

검색조건이 정상적으로 추가된 것을 확인할 수 있다.

Comments