JPA 에서 From 절에 서브쿼리 쓰기(@Subselect 통해서 view 생성)

JPA를 통해 개발하다 보면 정말 쿼리로 다 되는데.. querydsl 에서 구현 안되는 게 너무 많다.
정말 마이바티스 쓰고 싶어서 눈물이 좔좔좔
특히 서브쿼리가 문제인데.. 서브쿼리가 필요한 쿼리라면 객체지향적으로 잘못 설계된 거라고 하는데...
그래도 이제 와서 바꿀 수 없잖아요..?
그래서 서브쿼리 때문에 native query를 쓰기엔 동적 조건 표현하기가 힘들어서

< if test="title != null" > 내놔..

열심히 찾아본 결과 @Subselect를 발견하게 되었다.

 

@Subselect
어노테이션은 엔티티 클래스에 적용되며, SQL 서브쿼리를 사용하여 가상 뷰를 정의하는 데 사용됩니다. 이 가상 뷰는 데이터베이스에 실제로 존재하지 않는
테이블처럼 사용할 수 있으며, 복잡한 쿼리를 간단하게 만들거나, 여러 테이블을 조인한 결과를 하나의 엔티티로 매핑하는 등의 용도로 활용할 수 있습니다.

 

@Entity
@Subselect("SELECT u.id AS id, u.name AS name, o.order_date AS orderDate FROM user u JOIN order o ON u.id = o.user_id")
public class UserOrderInfo {
    @Id
    private Long id;
    private String name;
    private Date orderDate;

}

@Subselect를 사용하면 사용자 정보와 주문 내역을 조인한 결과를 가지는 가상의 뷰를 엔티티로 매핑할 수 있다. @Subselect 안의 쿼리의 결과로 하나의 엔티티가 만들어지는 거기 때문에 복잡한 query 도 가능하다. 단점은 저 쿼리 안의 변수는 사용할 수 없다.

public class UserOrderRepositoryImpl implements UserOrderRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    public UserOrderRepositoryImpl(EntityManager entityManager) {
        this.queryFactory = new JPAQueryFactory(entityManager);
    }

    @Override
    public List<UserDto> getUser() {
        QUserOrderInfo userOrderInfo = QUserOrderInfo.userOrderInfo;

        return queryFactory
                .select(Projections.bean(UserDto.class,
                        userOrderInfo.id.as("userId"),
                        userOrderInfo.name,
                        userOrderInfo.orderDate))
                .from(userOrderInfo)
                .fetch();
    }
}

이렇게 하면 콘솔에 찍히는 쿼리는 이렇게 나온다.

SELECT
    user_order_info.id AS userId,
    user_order_info.name,
    user_order_info.order_date
FROM
    (SELECT u.id AS id, 
            u.name AS name, 
            o.order_date AS orderDate 
       FROM user u JOIN order o 
         ON u.id = o.user_id) AS user_order_info

예시를 위해 from 절에 서브쿼리를 썼지만 join 으로도 가능하고 여러가지 장점이 있다.
나는 Entity 패키지 밑에 view 패키지를 따로 둬서 관리하니까 모아보기도 편했다.

이 방법의 단점으로 찾아본 것은

  1. 성능저하 : 서브쿼리가 너무 복잡하거나 데이터가 많은 경우, 성능에 부정적인 영향을 줄 수 있습니다.
  2. 작업 제한 : 가상 뷰는 데이터베이스에 실제로 존재하지 않으므로, 업데이트나 삭제 등의 작업에 제한이 있을 수 있습니다.
    이런 것들이 있으나.. 성능저하 정도될까지 아직 써보지 않았고 view 테이블이기 때문에 Only READ 기능으로만 사용하고 있어서 큰 불편함 없이 쓰고 있다.

정말 부득이하게 from 절에 subquery가 필요하다면 강추 !