코딩마을방범대

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

💡 백엔드/Java

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

신짱구 5세 2024. 7. 23. 15:17
728x90

 

 

 

JpaSpecificationExecutor

 

  • Spring Data JPA에서 제공하는 인터페이스
  • DB 쿼리의 조건을 간단히 Spec으로 작성하여 날릴 수 있게 해줌
    (Spec으로 관리하게 되면 코드가 깔끔해지며 유지보수가 좀 더 용이해짐)
  • JPA Criteria API를 활용하여 복잡한 동적 쿼리를 작성할 수 있음

 


주요 메서드

유형 메서드 설명
List<T> findAll(Specification<T> spec) 엔티티 조회
Page<T> findAll(Specification<T> spec, Pageable pageable) 엔티티 조회
List<T> findAll(Specification<T> spec, Sort sort) 엔티티 조회
Optional<T> findOne(Specification<T> spec) 단일 엔티티 조회
<S extends T,R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>,R> queryFunction) 결과 유형을 정의하여 엔티티 조회
long count(Specification<T> spec) 엔티티 개수 조회
long delete(Specification<T> spec) 엔티티 삭제 (삭제된 행의 개수 반환)
boolean exists(Specification<T> spec) 일치하는 요소가 있는지 확인

 

 


 

 

※ JpaSpecificationExecutor, Specification, Predicate의 연관성

 

JpaSpecificationExecutor은 구상한 spec을 적용시키는 인터페이스일 뿐이다.

따라서 조건을 별도로 설정하여 인수로 전달해주어야 한다.

 

일단 아래의 개념을 정리하고 넘어가야 이해가 쉬울 것 같다.

Specification 의 메서드들을 통해 조건 객체(Predicate)를 생성하고, 조건을 수집한다.

모여진 조건들(Specification<객체>)을 JpaSpecificationExecutor를 구현한 repository에 넘겨줄 수 있다.

 

인터페이스 설명
JpaSpecificationExecutor Spring Data JPA에서 제공하는 인터페이스
Specification JPA Criteria API를 사용하여 동적인 쿼리를 생성하는 데 사용
Predicate JPA Criteria API에서 쿼리의 WHERE 절을 나타냄

 

 

 

 

 


 

 

 

 

 

 

Specification

JPA Criteria API를 사용하여 동적인 쿼리를 생성하는 데 사용한다.

Root, CriteriaQuery, CriteriaBuilder를 인자로 받아 Predicate를 반환한다.

 

 


주요 메서드

메서드 설명
and(Specification<T> other) 두 개의 Specification을 AND 연산하여 새로운 Specification을 생성
or(Specification<T> other) 두 개의 Specification을 OR 연산하여 새로운 Specification을 생성
not(Specification<T> other) 주어진 Specification의 반대 조건을 가진 새로운 Specification을 생성
where(Specification<T> other) 주어진 Specification을 현재 Specification에 추가
having(Specification<T> other) 그룹화된 데이터에 대한 조건을 추가

 

 


 

 

toPredicate()의 인자

 

1. Root<T> root

JPA Criteria API에서 쿼리의 루트 엔티티를 나타낸다.

(쿼리의 FROM 절에 해당하는 엔티티를 의미)

이 인터페이스를 통해 엔티티의 속성에 접근할 수 있다.

 

메서드명 설명
get(String attributeName) 엔티티의 특정 속성에 접근할 수 있음
join(String associationAttributeName) 엔티티의 연관 관계 속성에 접근할 수 있음

 

 


 

2. CriteriaQuery<?> query

JPA Criteria API에서 쿼리 자체를 나타낸다.

쿼리의 SELECT, FROM, WHERE, ORDER BY 등의 절을 구성할 수 있다.

 

메서드명 설명
select(Expression<T> selection) 쿼리의 SELECT 절을 설정
where(Predicate... restrictions) 쿼리의 WHERE 절을 설정
orderBy(Order... orders) 쿼리의 ORDER BY 절을 설정

 


 

3. CriteriaBuilder criteriaBuilder

 

JPA Criteria API에서 쿼리 조건을 생성하는 데 사용한다.

다양한 비교 연산자(equal, greaterThan, like 등)와 논리 연산자(and, or, not 등)를 제공한다.

 

 

3-1. 비교 연산자 메서드

메서드명 설명
equal(...) 같음 비교
notEqual(...) 같지 않음 비교
greaterThan(...) 크다 비교
greaterThanOrEqualTo(...) 크거나 같다 비교
lessThan(...) 작다 비교
lessThanOrEqualTo(...) 작거나 같다 비교
between(...) 범위 비교

 


 

3-2. 문자열 연산자 메서드

메서드명 설명
like(...) 문자열 패턴 비교
notLike(...) 문자열 패턴 비교 (부정)
startsWith(...) 문자열 시작 비교
endsWith(...) 문자열 끝 비교
contains(...) 문자열 포함 비교

 


 

3-3. 논리 연산자 메서드

메서드명 설명
and(...) 논리 AND 연산
or(...) 논리 OR 연산
not(...) 논리 NOT 연산

 


 

3-4. 기타 메서드

메서드명 설명
isNull(...) NULL 비교
isNotNull(...) NULL 아님 비교
in(...) 집합 비교
notIn(...) 집합 비교 (부정)
isEmpty(...) 비어있는지 비교
isNotEmpty(...) 비어있지 않은지 비교

 

 

 

 

 


 

 

 

 

 

 

사용 예제

 

1. Repository에 상속받기

@Repository
public interface TestRepository extends JpaRepository<TestEntity, Long>, JpaSpecificationExecutor<TestEntity> {
    // ...
}

 

 


 

 

2. 쿼리 작성하기

※ 이 때 Specification의 toPredicate() 인자를 활용하면 된다.

 

2-1. 단일 Predicate 반환

단일 반환은 criteriaBuilder.메서드()를 그대로 리턴하면 된다.

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

 


2-2. 다중 Predicate 반환

다중 반환은 criteriaBuilder.and()를 이용해 Predicate 리스트를 반환하면 된다.

public Specification<TestEntity> equalTest(Long id, String name) {
	return new Specification<TestEntity>() {
		@Override
		public Predicate toPredicate(Root<TestEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
			List<Predicate> predicates = new ArrayList<>();
			predicates.add(criteriaBuilder.equal(root.get("id"), id));
			predicates.add(criteriaBuilder.like(root.get("name"), "%" + name));
			return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
		}
	};
}

 

번외. 익명 사용

위와 같이 override 하지 않고 직접 익명으로 사용하는 방법이 있다.
Specification<TestEntity> spec = (root, query, criteriaBuilder) -> {
	List<Predicate> predicates = new ArrayList<>();
	predicates.add(criteriaBuilder.equal(root.get("id"), id));
	predicates.add(criteriaBuilder.like(root.get("name"), "%" + name));
	return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};

 

 

 


 

 

3. 쿼리 등록하기

※ 이 때 Specification의 주요 메서드를 활용하면 된다.

 

3-1. 단일 쿼리 등록하기

Specification<TestEntity> spec = Specification.where(equalTest(id));

 


3-2. 다중 쿼리 등록하기

where을 통해 초기화된 spec에 and를 통해 쿼리를 추가해주면 된다.

Specification<TestEntity> spec = Specification.where(equalTest(id));
spec = spec.and(likeContents(contents));

(예시 설명: likeContents()는 equalTest()와 마찬가지로 Predicate를 반환하는 메서드다.)

 

 

 


 

 

4. DB 쿼리 실행

※ 이 때 JpaSpecificationExecutor의 주요 메서드를 활용하면 된다.

 

TestRepository.findAll(spec);

 

 

 

 


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

728x90