개린이 탈출기

[Oracle] SQL 쿼리문의 동작을 예측하며 JPA로 변환하기 본문

카테고리 없음

[Oracle] SQL 쿼리문의 동작을 예측하며 JPA로 변환하기

yooverd 2024. 4. 4. 00:41
728x90
반응형
SMALL

학원에서 sql쿼리문을 jpa로 변환하는 실습을 할 때는 아주 기초적인 쿼리문을 jpa로 변환했었기 때문에 별다른 고민 없이 직독직해 수준으로 쿼리메서드를 써내려갔었다.

그러나 지금은 MSA 기반에서 더욱 복잡한 SQL 쿼리문을 JPA로 변환하면서 많은 생각을 하게 되었다.

쿼리문의 실행 순서 혹은 사용자가 어떤 방식으로 서비스를 사용할 지 등을 고려하며 들었던 생각들을 이곳에 정리해두려 한다.

참고로 이제 개발을 시작한 지 갓 1개월이 된 상태에서 작성하는 글이기도 하고 아직 JPA가 무엇인지 조차 개념이 잡히지 않은 상태이니 ...

만약 누군가가 이 글을 보게 된다면 이 점을 참고해주었으면 한다.

 

참고로 후술할 내용은 MSA 기반의 애플리케이션에서 SQL -> JPA로의 변환에서 고민한 내용들이므로 비슷한 환경이 아니라면 전혀 도움이 되지 않을 수 있다.

 

0. 전혀 감이 오지 않는다면 실행계획을 우선 살펴보자!

  Dbms 도구의 실행계획을 살펴보면서 나보다 더 똑똑한 컴퓨터님은 쿼리문을 어떤 순서로 실행하실지 염탐하면서 생각의 틀을 그려나가는 것이 큰 도움이 되었다. 실행 순서를 읽어보면서 왜 이런 판단을 했는지 생각해보면서 일하다 보니 나중에는 혼자서도 틀을 그리는 힘을 기를 수 있었다. 

 

1. 여러 테이블이 조인될 때는 기준 테이블이 무엇인지 판단을 해보아야 한다.

  두 개 이상의 테이블이 조인될 때 어떤 테이블부터 조건을 시작할 지 막막할 때 해 본 생각이다.

  조건의 개수와 상관없이 조인되어 완성될 테이블의 모수를 크게 줄여줄 수 있는 조건이 걸린 테이블을 기준 테이블이라고 생각하면 되는 것 같았다. 조건에 걸린 컬럼이 pk 인 경우, uk인 경우,컬럼 값의 종류가 많지 않다거나 하는 등의 예시가 있을 것 같다. 이렇게 모수를 크게 줄여줄 수 있는 조건부터 실행될 것이니 이 순서에 맞추어 개발하면 실행순서가 불일치하여 결과값이 달라지는 문제를 조금은 막을 수 있다.

 

2. With절에 걸린 가상테이블은 서브쿼리라고 생각해도 괜찮을지도 모른다.

   대뜸 with절에 작성된 가상테이블이 5-6개가 나와서 크게 당황했던 기억이 난다. 이 때는 당황하지말고 해당 가상테이블을 그대로 잘라내어 약어로 작성된 부분에 그대로 붙여놓고 보아도 된다...

 

3. 그 가상테이블이 반환하는 행 수가 지나치게 많은데 조건이 거의 없거나 사실상 전체조회로 보일 때는 ...

  해당 가상테이블이 where 절에서 사용되는지, select절에서 사용되는지 확인해보자.

   특히나 select 절에서 사용된다면 select 절에서 사용될 때 까지 해당 가상테이블의 컬럼과 조인조건으로 걸린 컬럼의 조건까지 살펴보면 좋다. 만일 이런 조건이 존재한다면 해당 컬럼의 값을 받아와 가상테이블에도 해당 조건을 추가하여 반환하는 행 수를 줄여줄 수 있다.  즉, 가상테이블과 조금이라도 연관성을 가지고 있는 (조회에 영향을 미치는) 조건들을 찾아내어 추가하는 것이다.

   Where 절에서 사용된다면.. 이 케이스는 아직 만나보지 못했다. 만일 나중에 발견하게 된다면 그 때 다시 정리해보겠다.

 

4. 조회를 위해 전달받는 파라미터가 다수인 경우, 컬럼를 쌍으로 조회해야 할 때

   이 부분은 Querydsl과 조금 더 관련이 있는 부분이긴 하다.

   만일 쌍으로 비교해야하는 문제를 해결하기 위해 각 컬럼에 in을 이용하여 따로 처리하면 문제가 발생할 수 있다. 

 

   (적당히 각각 비교했을때의 경우와 쌍으로 비교했을 떄의 차이를 그림으로 그려서 설명하자)

 

   위와 같이 쌍으로 비교하면 2개의 경우가 되지만, 각각 in 으로 비교하게되면 4개의 경우가 되어 조회 결과에 차이가 발생할 수 있다. 따라서 이 경우엔 아래와 같이 쌍으로 비교하도록 코드를 짜줘야 한다.

WHERE ( frst_col = 'a' AND sec_col = '1' )
OR ( frst_col = 'b' AND sec_col = '2' )

 

위의 코드를 작성하기 위해 Querydsl을 사용했는데, 나는 참고로 아래와 같이 작성했다.

@Repository
public class TestTableCustomStore {

    private final JPAQueryFactory jpaQueryFactory;

    public TestTableCustomStore(JPAQueryFactory jpaQueryFactory) { this.jpaQueryFactory = jpaQueryFactory; }

    public List<testData> findByfrstColAndLastColIn(List<ColsDto> conditions) {
        QTestTableJpo qTestTableJpo = QTestTableJpo.testTableJpo;
        JPAQuery<TestTableJpo> testTableJpoJPAQuery = jpaQueryFactory.selectFrom(qTestTableJpo);

        Expression[] conditionExpressionArray = conditions.stream().map(condition -> {
            return Expressions.template(Object.class, "(({0}, {1}))", condition.getFrstCol(), condition.getSecCol());
        }).toArray(Expression[]::new);

        List<TestTableJpo> testTableJpo = tesTableJpoJPAQuery
                .where(Expressions.list(qTestTalbleJpo.frstCol, qTestTalbleJpo.secCol).in(conditionExpressionArray))
                .fetch();
        return TestTableJpo.toDomains(testTableJpos);
    }

}

 

 

 

728x90
반응형
LIST