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 패키지를 따로 둬서 관리하니까 모아보기도 편했다.
이 방법의 단점으로 찾아본 것은
- 성능저하 : 서브쿼리가 너무 복잡하거나 데이터가 많은 경우, 성능에 부정적인 영향을 줄 수 있습니다.
- 작업 제한 : 가상 뷰는 데이터베이스에 실제로 존재하지 않으므로, 업데이트나 삭제 등의 작업에 제한이 있을 수 있습니다.
이런 것들이 있으나.. 성능저하 정도될까지 아직 써보지 않았고 view 테이블이기 때문에 Only READ 기능으로만 사용하고 있어서 큰 불편함 없이 쓰고 있다.
정말 부득이하게 from 절에 subquery가 필요하다면 강추 !