QueryDSL , JPAQueryFactory CustomTemplate for MySQL

Spring JPA에서 queryDsl을 사용하는 중 mysql 에서 NumberExpression.random() 이 “random()”함수로 호출되는 문제에 대하여.

PostgreSQL 에서 MySql 로 DB를 변경하는 요건이 생겼다.

QueryDSL의 JPAQueryFactory 사용하여 query를 조회하는 부분에서
NumberExpression.random() 이 제대로 조회되지 않는다.

NumberExpression.random()이 변환되는 sql을 보니 random() 인데

DB 랜덤함수에 대해보자면.
PostgreSQL에는 random()으로 지원되고.
MySQL의 경우 rand() 으로 지원된다.

MySQL에서는 random() 함수가 없기때문에 에러가 발생한다.

JPAQueryFactory는
삭제시 JPADeleteClause
수정시 JPAUpdateClause
조회시 JPAQuery
객체를 사용하는데. JPAQueryFactory 생성자 메소드에 별도로 지정한 JPQLTemplates 를 사용하지 않는다면.

내부에서 JPAProvier를 통해 JPQLTemplate를 가져온다.

Image for post
Image for post

JPAProvider 내부적으로 알맞은 JPQLTemplates를 지정하는데.
hibernate JPA의 경우 EntiryManager.getDelegate().getClass()를 통해
org.hibernate.ejb.HibernateEntityManager 와 매칭된다.

Image for post
Image for post

여기서 HQLTemplates를 보게되면.
JPQLTemplates 를 상속하여 구현하고 있고.
JPQLTemplates 의 부모 객체인 Templates에는 아래와 같이 Random이 지정되어있다.

Image for post
Image for post

… 어쩌지..

해결을 위해 일단 HQLTemplate를 상속받는 MySqlTemplates를 작성하였다.
그리고 랜덤함수에 대한 부분을 추가.
참고

JPAQueryFactory를 재작성 또는 상속받아 랩핑하였다.
(내부적으로 JPAQueryFactory를 빈으로 등록하여 사용중이므로 별로 클래스로 재작성하지 않고 , 상속받아 랩핑하였다.)

query(), update(), delete() 메소드에 대한 재작성이다.
단순히 지정된 템플릿이 없을 경우 , default로 JPAProvider 를 통해 설정되기전에 작성한 MySqlTempaltes 를 주입하고 있다.

HQLTemplates 베이스이기때문에 HQLTemplates 를 사용하는 부분인지 확인
하고, EntityManager 의 session dialect() 를 확인하여 MySQLDialect라면
MySQLTemplate를 사용하도록 한다.

— 정리하며 생각해보니, SessionFactory 자체가 hibernate 쪽이라..
구지 HQLTemplates 확인 안해도 exception나서 처리가 안될것 같다.
HQLTemplate체크 if문의 제거해도 상관 없을 듯.

-_- 올바른 방법인지는 모르겠다.
SQLQueryFactory쪽에서도 configuration을 통해 dialect를 직접 지정해야 하는 걸로 보이고,
JPAQueryFactory 쪽에는 Dialect에 대한 처리가 보이지 않아서.. 멘붕오다가
빠르게 해야하기 때문에 직접 처리를 했다.

이 글을 혹시나 보신다면.. 좋은 방법좀 알려주세요.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store