코딩마을방범대
[Java] JPA Specification 이용하여 쿼리 조건 다루기 본문
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);
'💡 백엔드 > Java' 카테고리의 다른 글
[Java] @Query 어노테이션 (0) | 2024.07.23 |
---|---|
소나큐브 7.2.1 버전에서 새로운 프로젝트 등록하기 (1) | 2024.06.28 |
소나큐브 코드스멜 테스트 결과 메시지 분석 (0) | 2024.06.27 |
직렬화(Serialization) 란? (0) | 2024.06.27 |
JAVA의 try-with-resources (0) | 2024.06.25 |