[Spring / 스프링] querydsl projection 에서 서브쿼리 사용하기

반응형

Querydsl 로 db에서 데이터를 가져오는 로직이 있는데, 

left join 을 여러번해서 데이터를 가져오다보니 쿼리가 10초 이상 걸려서 db 커넥션 타임아웃이 나는 문제가 발생하여...

이를 개선하는 과정에서 querydsl projection 에서 subquery를 사용하기로 했다.

 

NumberPath<Long> bCounts = Expressions.numberPath(Long.class, "bCounts");

QAEntity aEntity = QAEntity.aEntity;
QBEntity bEntity = QBEntity.bEntity; // left join 한 table

Projections.bean(
	AEntity.class,
    aEntity.id,
    aEntity.name,
    aEntity.age,
    aEntity.school,
 	bEntity.countDistinct().as(bCounts)
)

 

기존에는 위와 같이 AEntity 에 BEntity 를 left join 한 결과를 countDistinct() 로 개수를 bCounts 라는 컬럼으로 내려주고 있었다.

실제 내려주는 데이터는 다른 left join 하는 테이블이 많아서 쿼리가 너무 오래 걸리는 문제가 있었다.

 

count 값을 가져오는게 필요하기 때문에 이를 해결하고자

count 값만 가져오는 subquery 를 이용하여 bCounts 컬럼으로 내려주도록 했다.

 

서브쿼리를 사용하려면 JPAExpressions 를 이용하여 JPQLQuery 를 생성해서 사용하면 된다

JPQLQuery<Long> bSubquery = JPAExpressions.select(bEntity.id.count())
            .from(bEntity)
            .where(bEntity.parent.id.eq(aEntity.id));

 

이렇게 서브쿼리를 만드는건 성공했는데, 이걸 NumberExpression 으로 만드는 과정에서 애를 좀 먹었다.

하지만 ExpressionUtils.as, Expressions.asNumber 를 이용해서 해결할 수 있었다.

 

이를 적용한 코드를 살펴보자

NumberPath<Long> bCounts = Expressions.numberPath(Long.class, "bCounts");

QAEntity aEntity = QAEntity.aEntity;
QBEntity bEntity = QBEntity.bEntity;

// 서브쿼리 이용
JPQLQuery<Long> bSubquery = JPAExpressions.select(bEntity.id.count())
            .from(bEntity)
            .where(bEntity.parent.id.eq(aEntity.id));

// NumberExpression 으로 변환
NumberExpression<Long> bCountsExpression = Expressions.asNumber(ExpressionUtils.as(bSubquery, bCounts));

Projections.bean(
	AEntity.class,
    aEntity.id,
    aEntity.name,
    aEntity.age,
    aEntity.school,
 	bCountsExpression, // 적용
)

 

개선 결과를 계산하기 위해 타임아웃 나는 데이터를 빼고 조회를 실행해보았다.

8~9초 걸리는 결과를 200ms ~ 1초 이내로 개선할 수 있었다.

반응형