본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
3장 : 데이터베이스와 트랜잭션

Spring Data Repository와 조건 조회

entity를 만들었다고 조회 코드가 저절로 좋아지지는 않습니다. 과목, 상태, 날짜를 모두 메서드 이름에 넣으면 조건 조합이 늘어날수록 Repository가 폭발합니다. Spring Data의 기본 Repository는 저장 경계로 두고, 변하는 검색 조건은 Specification으로 조합합니다.


Repository는 컬렉션처럼 보이는 영속 경계다

JpaRepository는 컬렉션처럼 보이지만 호출 하나마다 영속 컨텍스트와 SQL이 따라오는 I/O 경계입니다. 따라서 반환된 entity를 임의의 위치에서 수정하거나 findAll() 뒤에 Java로 필터링하는 습관을 피해야 합니다. 조건은 DB로 보내고, 정렬은 페이징 전에 고정합니다.


과목·상태·날짜 조건을 조합한다

Repository interface에는 entity의 타입과 ID 타입만 고정합니다. JpaSpecificationExecutor를 함께 상속하면 서비스가 필요한 조건만 골라 and로 묶을 수 있습니다. null은 “조건 없음”으로 해석하고 conjunction()을 반환해야 선택 조건이 자연스럽게 조합됩니다.

src/main/java/com/andongmin/studylog/session/StudySessionJpaRepository.java
package com.andongmin.studylog.session;

import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface StudySessionJpaRepository
        extends JpaRepository<StudySessionEntity, UUID>,
        JpaSpecificationExecutor<StudySessionEntity> {
}
src/main/java/com/andongmin/studylog/session/StudySessionSpecifications.java
package com.andongmin.studylog.session;

import java.time.LocalDate;
import org.springframework.data.jpa.domain.Specification;

public final class StudySessionSpecifications {
    private StudySessionSpecifications() {
    }

    public static Specification<StudySessionEntity> subject(String subject) {
        return (root, query, builder) -> subject == null || subject.isBlank()
                ? builder.conjunction()
                : builder.equal(root.get("subject"), subject);
    }

    public static Specification<StudySessionEntity> onOrAfter(LocalDate from) {
        return (root, query, builder) -> from == null
                ? builder.conjunction()
                : builder.greaterThanOrEqualTo(root.get("studyDate"), from);
    }
}

정렬과 결과 SQL을 함께 확인한다

먼저 컴파일해 method signature와 metamodel 사용이 맞는지 확인합니다. 실제 SQL은 ch3-4의 PostgreSQL을 올린 뒤 spring.jpa.show-sql=true로 보는 편이 정확합니다. 여러 행의 studyDate가 같을 수 있으므로, 나중에 페이징을 붙일 때는 id를 두 번째 정렬 기준으로 추가해야 페이지 사이에서 행이 흔들리지 않습니다.

./mvnw -q -DskipTests compile
Sort 확인 결과
JpaRepository 기본 연산과 Specification 조합이 타입 오류 없이 컴파일되어야 합니다.

조회 경계를 얻었으니 이제 여러 Repository 호출과 상태 변경을 하나의 업무 단위로 묶을 차례입니다.