본문 바로가기

Programing/Framework

[Spring JPA] 멀티컬럼 - 쿼리메소드 vs @Query

도비님이 코드리뷰 PR을 주셨다.

기존에 query method로만 사용을 했는데 @Query 를 사용하여 왜 그랬는지 궁금했다.

알고보니 특정 컬럼에 대하여 or 조건으로 결합이 되었던 것이 이유였다.

예를 들어 (t.a = 'abc' or t.a = 'bcd') and ... 같이 우선적으로 적용해야 하는 or가 있는 경우였다.

물론 결합법칙으로 (A OR B) AND C 를 A AND C OR B AND C 로 풀 수는 있지만 trade-off 비용이 더 안좋았을 거라 생각이든다.

다른 생각

하지만 JPA 객체지향이 아닌 쿼리지향으로 사용하고 있지 않는가 하는 생각을 하게 되었다.
query method가 점점 길어지고 복잡해지고 있다면 쿼리지향을 하고 있는가 생각해 보는 점검의 시간(냄새)으로 생각하면 어떨까?

a라는 컬럼이 포함된 List<Entity>를 받고 자바에서 해당 조건을 처리하는 제 3안도 있었을 텐데 말이다.

Stackoverflow

- Spring data jpa - How to combine multiple And and Or through method name

- Multi-Column Search with Spring JPA Specifications


쿼리 메서드의 경우 쿼리를 서술식으로 만드는 과정이라 메서드 이름이 쿼리와 유사하다.

쿼리가 길어질 수록 메서드 이름도 길어진다.

가령 다음과 같은 이름의 쿼리 메서드가 있을 수 있다.

public interface IssueRepository extends CrudRepository<IssueEntity, Long> {
    List<IssueEntity> findAllByIssueProcessStatusAndIssueDateStartingWith(
            IssueProcessStatus issueProcessStatus, String approvedDate);
}

 

쪼개어보면 findAll By IssueProcessStatus And CashReceiptIssueDate Starting With ...

인데 쿼리로 변환되면 아래와 같은 식이 될 것이다.

select * from ISSUE where ISSUE_PROCESS_STATUS = 'REGISTER_STANDBY' and ISSUE_DATE like 'yyyyMMdd%';

하지만 메서드명이 길어서 잘 알아보기 어렵다.

나의 경우는 이렇게 긴 메서드를 바로 사용하기 보다는 목적이나 용도에 맞게 별칭(alias)를 지어서 사용한다.

@Repository
public class IssueResultRepository {
    private final IssueRepository issueRepository;

    public IssueResultRepository(IssueRepository issueRepository) {
        this.issueRepository = issueRepository;
    }

    public List<IssueEntity> findAllSuspendedIssuesAt(LocalDate targetDate) {
        return issueRepository.findAllByIssueProcessStatusAndIssueDateStartingWith(
                REGISTER_STANDBY, targetDate.format("yyyyMMdd"));
    }
}

IssueResultRepository 가 suspendedIssue라는 개념을 내부적으로 사용하는 REGISTER_STANDBY 를 숨기는 동시에 날자포멧에 대한 내용을 책임지고 있다.

사용하는 클라이언트는 내부적으로 날짜 타입이 TIMESTAMP인지, VARCHAR인지, VARCHAR라면 'yyyyMMdd' 형태로 저장했는지, 'yyyy-MM-dd' 형태로 저장했는지, 'yyyy-MM-dd HH:mm:ss' 로 저장했는지 알 필요가 없어진다.

 

하위 구현에 고민하지 않고 그냥 자바의 표준 날짜 타입인 LocalDate를 넘기면 된다.