: 하이버네이트는 JPA 확장 기능으로 @Subselect를 제공한다. @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;
...생략
protected OrderSummary() {
}
Java
복사
: @Immutable, @Subselect, @Synchronize는 하이버네이트 전용 애너테이션으로 해당 태그를 사용해서 테이블이 아닌 쿼리 결과를 @Entity로 매핑할 수 있다.
: @Subselect는 조회 쿼리를 값으로 가지는데 이렇게 조회한 @Entity는 매핑된 테이블이 없기 때문에 수정을 할 수 없다.
: 만약 수정을 하게 된다면 하이버네이트는 오류가 발생하게 된다. 이 때 @Immutable를 통해서 해당 엔티티 매핑 필드/프로퍼티가 변경되도 DB에 반영되지 않고 무시한다.
: 하이버네이트는 일반적으로 트랙잭션을 커밋하는 시점에 DB에 반영하는데 변경 내역을 아직 반영하지 않은 상태에서 다시 조회하게 되면 최신 값이 담기지 않게 된다.
: 이런 문제를 해소하기 위해서 @Synchronize를 사용하는데 @Synchronize에 해당 엔티티와 관련된 테이블 목록을 명시하고 엔티티를 로딩하기 전 지정한 테이블과 관련된 변경이 발생하면 플러시를 먼저한다.
Order order = orderRepository.findById(orderNumber);
order.changeShippingInfo(newInfo);
List<OrderSummary> summaries = orderSummaryRepository.findByOrdererld(userid);
Java
복사
: @Subselect를 사용해도 일반 @Entity와 같기 때문에 EntityManger#find(), JPQL, Criteria를 사용해서 조회하는 장점이 있다. 스펙또한 사용이 가능하다.
Specification<OrderSummary> spec = orderDateBetween(from, to);
Pageable pageable = PageRequest.of(1, 10);
List<OrderSummary> results = orderSummaryDao.findAll(spec, pageable);
Java
복사
: 서브 쿼리를 사용하고 싶지 않다면 네이티브 SQL 쿼리를 사용하거나 마이바티스와 같은 별도 매퍼를 사용해서 조회 기능을 구현해야 한다.