[QueryDSL] from, join 절에 서브쿼리 사용하기
이전 포스팅에서 한 테이블 내에서 특정 값을 그룹핑하여 최댓값을 가진 row를 가진 방법을 보았었다.
이때 Row_Number를 사용하여 추출한 데이터가 가장 빠른 방법으로 보였는데, 이는 서브 쿼리로 해야 한다는 점이 있다.
하지만..
JPQL?hibernate?에 의해 where절과 select절에서만 서브 쿼리가 사용 가능하다고 한다..(내부적인 원리는 나중에 따로 공부해봐야겠다.)
참고 글 : https://stackoverflow.com/questions/60258377/querydsl-4-x-subqueries-in-the-from-clause
QueryDSL 4.x subqueries in the FROM clause
Are subqueries in the from clause supported in the latest QueryDSL version? I tried my best but could not find a solution for now.
stackoverflow.com
ㅇㅏ무튼... 어떻게든 querydsl에서 서브 쿼리를 사용해서 위 케이스를 풀어낼 수 없을까 생각하다가.. 엔티티 레벨(?)에서 설정할 수 있는 법이 없을까 고민하다가 우연히 봤었던 @SubSelect 옵션이 생각났다.
참고 글 : https://stackoverflow.com/questions/25226244/what-is-the-use-of-synchronize-in-hibernate
What is the use of @Synchronize in Hibernate
As per Hibernate documentaion, There is no difference between a view and a base table for a Hibernate mapping. This is transparent at the database level, although some DBMS do not support vi...
stackoverflow.com
마치 view처럼 사용할 수 있는 entity를 만드는 걸로 entity에 맵핑할 쿼리를 직접 작성하는 것이다.
/**
* Map an immutable and read-only entity to a given SQL select expression.
*
* @see Synchronize
*
* @author Sharath Reddy
*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface Subselect {
/**
* The query.
*/
String value();
}
주석의 내용과 같이 update될 수 없는 select용 엔티티이다.
@Entity
@Subselect(
"select " +
" ROW_NUMBER() over (PARTITION BY c.User_Seq ORDER BY Apply_Date desc, Config_Seq desc) as Row_Num, " +
" c.Config_Seq, " +
" c.User_Seq, " +
" c.Apply_Date " +
"from CONFIG c "
)
@Immutable
@Synchronize("CONFIG")
public class ConfigSubSelect {
@Column(name = "Row_Num")
private Long rowNum;
@Id
@GeneratedValue
@Column(name = "Config_Seq")
private Long configSeq;
@Column(name = "User_Seq")
private Long userSeq;
@Column(name = "Apply_Date")
private LocalDate applyDate;
}
이렇게 되면 테이블 단위가 아닌 subselect에 작성된 query로 결과가 엔티티에 맵핑되게 된다.
그리고 이 엔티티를 똑같이 querydsl에서 사용해보자.
query
.selectFrom(configSubSelect)
.where(configSubSelect.rowNum.eq(1L))
.fetch();
select configsubs0_.Config_Seq as config_s1_0_,
configsubs0_.Apply_Date as apply_da2_0_,
configsubs0_.Row_Num as row_num3_0_,
configsubs0_.User_Seq as user_seq4_0_
from (select ROW_NUMBER() over (PARTITION BY c.User_Seq ORDER BY Apply_Date desc, Config_Seq desc) as Row_Num,
c.Config_Seq,
c.User_Seq,
c.Apply_Date
from CONFIG c) configsubs0_
where configsubs0_.Row_Num = ?
실행결과의 query를 보면 서브 쿼리로 원하는 서브 쿼리가 나간 것을 확인할 수 있다.
querydsl은 jpql기반이기 때문에, JPQL 마찬가지로 동작될 것으로 예상되고, spring data jpa에서 제공하는 find~ 메서드 또한 동일하게 사용 가능하다.
물론, update나 insert는 불가능하고, 엔티티 파일을 하나 더 만들어야 하지만,
비즈니스적으로 서브 쿼리를 써야만 하는 테이블이라면 다른 곳에서도 똑같이 쓸 확률이 있기 때문에 만들어놓고 공통으로 사용해도 괜찮을 것 같다