도메인 주도 개발 5장

김재연·2025년 4월 27일
post-thumbnail

스프링 데이터 JPA를 이용한 조회 기능

검색을 위한 스펙

보통 검색조건이 고정되어 있고, 단순하다면 find로 선언해도 괜찮지만, 그렇지 않다면 동적으로 검색 조건을 수정할 수 있는 Speficiation을 사용하는 것도 좋은 선택이다.

이를 선언하는 방법은 다음과 같다.

public interface Speficiation<T> {
	public boolean isSatisfiedBy(T agg);
}

스펙을 리포지터리에 사용하면 agg는 애그리거트 루트가 될 것이다.
스펙을 DAO에 사용하면 agg는 검색 결과로 리턴할 데이터 객체가 되는 것이고 말이다. (여기서 말하는 DAO는 데이터 접근을 의미하는 것이라고 보면 된다.)

isSatisfiedBy의 반환은 true or false이고, 이는 검색 대상 객체가 조건을 충족하는지 안하는지를 구분하는 것이라고 볼 수 있다.

스펙에 대한 기본적인 패러다임은 인지했으니, Spring Data JPA를 활용해서 진행하는 방법을 알아보자.

스프링 데이터 JPA를 이용한 스펙 구현

이전에 isSatisfiedBy를 구현했던 것처럼, 동일하게 toPredicate를 구현하면 된다.

여기서 제네릭 타입은 JPA Entity가 되어야 한다.

그리고 코드를 잘 보면 OrderSummary_ 이렇게 되어 있는데, 이는 메타 모델을 의미한다. QueryDSL을 사용하게 되더라도 Q클래스가 나오게 되는데 이것과 비슷해보인다.

스펙을 하나하나 클래스로 구현하는 방법도 있지만, 그냥 static class가 스펙 함수를 반환하는 형식으로 구성해도 된다.

리포지터리/DAO에서 스펙 사용하기

실제로 사용하는 사례가 상당히 괜찮은 것 같다.
앞으로 조금 복잡한 쿼리가 존재한다면 이렇게 진행해봐야겠다.

스펙 조합

스펙에 and 및 or을 디폴트 메서드로 제공하고 있어, 여러 스펙을 합치는데 굉장히 용이하다.

또한, 스펙 인터페이스는 not, where을 제공해 조건을 반대로 뒤집을수도, 혹은 null인 조건들은 그냥 넘어가도록 진행할수도 있다. (null이 아니라면 조건 수행)

정렬 지정하기

JPA는 정렬 기준 프로퍼티가 두 개 이상이면 메서드 이름이 굉장히 길어진다.

또한, 상황에 맞춰서 정렬 순서도 변경할 수 없기 때문에, Sort 타입을 이용해서 정렬할 수 있다.

이런 식으로 Specification 과 동일하게 Sort도 여러개를 합쳐서 진행할 수 있다.

페이징 처리하기

이런 식으로 Pageable을 사용할 수 있고

반환된 것으로 다음과 같이 활용할 수 있다

이를 스펙과 같이 사용할 수도 있다.

스펙 조합을 위한 스펙 빌더 클래스

스펙을 조건에 따라 조합해야 할 때가 있는데, 이 때 if로 연결하는 것이 아닌, SpecBuilder를 사용하면서 가독성 좋게 합칠 수 있다.

다만 toSpec을 보면 알 수 있듯이, 이 조건들이 and로 이어진다는 것을 유의하면 좋을 것 같다.

동적 인스턴스 생성

JPQL에서 new 키워드를 사용해 객체를 생성할 수 있다.

하이버네이트 @Subselect 사용

@Subselect는 쿼리를 명시하고, 그 내부에 필드를 명시하여, 가상의 테이블을 하나 만든다고 생각하면 괜찮을 것 같다. (하지만 실제로 테이블은 존재하지 않는다)

그렇기 때문에, 필드에 변경사항이 일어났을 때, update가 일어나 변경사항을 DB에 flush하게 되면, 테이블이 없으므로 에러가 발생한다 그렇기 때문에, @Immutable 어노테이션을 활용해 변경되더라도 flush가 일어나지 않도록 한다.

또한, 변경 내역이 DB에 반영되기 이전에(트랜잭션 커밋이전), 변경된 데이터를 조회하고 싶다면 @Synchronize 어노테이션을 활용해 내부에 테이블 명들을 명시해놓으면, 명시해놓은 테이블에 변경사항이 생겼을 때 이를 반영하여, 변경된 데이터를 불러올 수 있게끔 해준다.

따라서 조회시점에 업데이트가 발생하는 것이다.

Subselect를 활용하더라도 이전과 같이 spec, page 등은 활용할 수 있으며, 조회 시에 from 절에 subquery(없는 테이블이니까)가 발생한다는 것에 유의하자

profile
끊임없이 '성장'하는 개발자 김재연입니다.

0개의 댓글