새로 추가한 밸류 타입을 알맞는 형식으로 출력하지 못하므로 값을 기본 타입으로 변환하면 편리
public interface OrderSummaryDao extends Repository<OrderSummary, String> {
@Query("""
select new com.myshop.order.query.dto.OrderView(
o.number, o.state, m.name, m.id, p.name
)
from Order o join o.orderLines ol, Member m, Product p
where o.orderer.memberId.id = :ordererId
and o.orderer.memberId.id = m.id
and index(ol) = 0
and ol.productId.id = p.id
order by o.number.number desc
""")
List<OrderView> findOrderView(String ordererId);
}
...
public class OrderView {
private final String number;
private final OrderState state;
private final String memberName;
private final String memberId;
private final String productName;
public OrderView(OrderNo number, OrderState state, String memberName, MemberId memberId, String productName) {
this.number = number.getNumber();
this.state = state;
this.memberName = memberName;
this.memberId = memberId.getId();
this.productName = productName;
}
...
}
동적 인스턴스의 장점은 JPQL을 그대로 사용하므로 객체 기준으로 쿼리를 작성하면서도 동시에 지연/즉시 로딩과 같은 고민 없이 원하는 모습으로 데이터를 조회할 수 있다는 점이다.
하이버네이트 @Subselect 사용
하이버네이트는 JPA 확장 기능으로 @Subselect를 제공`
쿼리 결과를 @Entity로 매핑할 수 있는 유용한 기능
@Entity
@Immutable
@Subselect(
"""
select o.order_number as number,
o.version,
o.orderer_id,
o.orderer_name,
o.total_amounts,
o.receiver_name,
o.state,
o.order_date,
p.product_id,
p.name as product_name
from purchase_order o inner join order_line ol
on o.order_number = ol.order_number
cross join product p
where
ol.line_idx = 0
and ol.product_id = p.product_id"""
)
@Synchronize({"purchase_order", "order_line", "product"})
public class OrderSummary {
@Id
private String number;
private long version;
@Column(name = "orderer_id")
private String ordererId;
@Column(name = "orderer_name")
private String ordererName;
...
@Immutable, @Subselect, @Synchronize는 하이버네이트 전용 애노테이션인데, 이 태그로 테이블이 아닌 쿼리 결과를 @Entity로 매핑 가능하다.
@Subselect
조회 쿼리를 값으로 갖는다.
select 쿼리 결과를 매핑할 테이블처럼 사용(DBMS의 뷰와 유사)
뷰와 동일하게 @Subselect로 조회한 @Entity는 수정 불가
@Immutable
@Subselect를 이용한 @Entity의 매핑 필드 수정 시 하이버네이트가 변경 내역을 반영하여 update 쿼리를 실행하는 문제를 방지하기 위해 사용
매핑 테이블이 없으므로 에러가 발생하는데 이 문제를 방지
@Immutable를 사용하면 해당 엔티티의 매핑 필드/프로퍼티가 변경되어도 DB에 반영하지 않고 무시
@Synchronize
엔티티 상태 변경 내역이 아직 테이블이 반영되지 않은 상태(커밋 전 시점)에서 조회 시 최신 값이 아닌 이전 값이 담기는 문제를 해소하기 위해 사용
해당 엔티티와 관련된 테이블 목록 명시
하이버네이트는 엔티티 로딩 전에 지정한 테이블과 관련된 변경이 발생하면 flush 수행
.
@Subselect를 사용해도 일반 @Entity와 같으므로 EntityManager#find(), JPQL, Criteria, Spec을 사용한 조회가 가능